PSDSC.psm1

using namespace System.Text.Json.Serialization
#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/20.ConfigurationDocument.ps1' -1

#using namespace System.Text.Json.Serialization

class ConfigurationDocument
{
  [JsonPropertyName('$schema')]
  [System.String]
  $schema

  [JsonIgnoreAttribute(Condition = 'WhenWritingNull')]
  [System.Collections.Hashtable[]]
  $parameters

  [JsonIgnoreAttribute(Condition = 'WhenWritingNull')]
  [System.Collections.Hashtable[]]
  $variables

  [ConfigurationResource[]]
  $resources

  [JsonIgnoreAttribute(Condition = 'WhenWritingNull')]
  [System.Collections.Hashtable]$metadata

  ConfigurationDocument()
  {
  }
  ConfigurationDocument([string]$schema, [ConfigurationResource[]]$resources)
  {
    $this.schema = $schema
    $this.resources = $resources
  }

  [string] SerializeToJson()
  {
    $options = [System.Text.Json.JsonSerializerOptions]@{
      WriteIndented = $true
    }

    return [System.Text.Json.JsonSerializer]::Serialize[ConfigurationDocument]($this, $options)
  }

  [string] SerializeToYaml()
  {
    if (TestYamlModule)
    {
      return (ConvertTo-Yaml -InputObject $this -Depth 10)
    }
    else
    {
      throw "YamlDotNet module is not available. Please install the module to use this feature."
    }
  }
}

class ConfigurationResource
{
  [System.String]
  $name

  [System.String]
  $type

  [System.Collections.Hashtable]
  $properties

  ConfigurationResource()
  {
  }
  ConfigurationResource([string]$name, [string]$type)
  {
    $this.name = $name
    $this.type = $type
  }
  ConfigurationResource([string]$name, [string]$type, [hashtable]$properties)
  {
    $this.name = $name
    $this.type = $type
    $this.properties = $properties
  }
}
#EndRegion './Classes/20.ConfigurationDocument.ps1' 80
#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') -or $fakeBoundParameters.ContainsKey('ResourceType'))
# {
# [array]$Resources = GetDscRequiredKey -BuildHashTable | Where-Object {
# $_.type -eq $fakeBoundParameters.ResourceName -or $_.type -eq $fakeBoundParameters.ResourceType
# } | 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/ResourceCompleters.ps1' -1

class ResourceCompleter : 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
    )
    {
        function CreateCompletionResult($text)
        {
            $CompletionText = $text
            $ListItemText = $text
            $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue
            $ToolTip = $text

            return [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, $ResultType, $ToolTip)
        }

        $exe = Resolve-DscExe -ErrorAction SilentlyContinue

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

        if ($exe)
        {
            # section to include DSC resource data
            $manifestFiles = Get-ChildItem -Path (Split-Path $exe -Parent) -Depth 1 -Filter "*.dsc.resource.json"

            if ($manifestFiles.Count -ne 0)
            {
                $manifestFiles | ForEach-Object {
                    $typeName = (Get-Content $_ | ConvertFrom-Json -ErrorAction SilentlyContinue).type

                    if ($typeName)
                    {
                        $obj = CreateCompletionResult $typeName
                        $list.add($obj)
                    }
                }
            }

            # section to include PSTypes data
            $psTypes = Read-PsDscAdapterSchema -ReturnTypeInfo
            if (-not [string]::IsNullOrEmpty($psTypes))
            {
                $psTypes | ForEach-Object {
                    $list.add((CreateCompletionResult $_))
                }
            }
        }

        return $list
    }
}

# TODO: Move this to a function as Get-PsDscResourceExample
# class ResourceInputCompleter : 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('Resource'))
# {
# [array]$Resources = GetDscRequiredKey | Where-Object {
# $_.type -eq $fakeBoundParameters.Resource
# } | 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/ResourceCompleters.ps1' 97
#Region './Classes/VersionCompleters.ps1' -1

class VersionCompleter : 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 = (Get-DscVersion -UseGitHub) | Where-Object { $_ -like "$wordToComplete*" }

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

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

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

        return $list

    }
}
#EndRegion './Classes/VersionCompleters.ps1' 30
#Region './Private/DSC/Build-DscConfigDocument.ps1' -1

function Build-DscConfigDocument
{
    <#
    .SYNOPSIS
        Build DSC configuration document

    .DESCRIPTION
        The function Build-DscConfigDocument 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:\> Build-DscConfigDocument -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 = ConvertTo-DscObject @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/Build-DscConfigDocument.ps1' 100
#Region './Private/DSC/Confirm-DscConfigInput.ps1' -1

function Confirm-DscConfigInput
{
    <#
    .SYNOPSIS
        Private function to confirm the input provided by the user.

    .DESCRIPTION
        The function Confirm-DscConfigInput confirms the config input before invoking the DSC version 3 command-line utility.
    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .PARAMETER Operation
        The operation to be performed. Supports 'get', 'set', 'test', and 'export'.

    .PARAMETER Parameter
        The parameter to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .EXAMPLE
        PS C:\> Confirm-DscConfigInput -Inputs '{"keyPath":"HKCU\\1\\2"' -Operation 'get'

        This example returns the string 'config get --resource Microsoft.Windows/Registry --input {"keyPath":"HKCU\\1\\2"}'.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

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

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

        [Parameter()]
        [AllowNull()]
        [System.String]
        $Parameter
    )

    $sb = [System.Text.StringBuilder]::new('config')

    if (-not [string]::IsNullOrEmpty($Parameter))
    {
        $parameterInput = if (-not (Test-IsDscFilePath -Path $Parameter))
        {
            # Resolve the parameter input and append it to the command
            $Parameter = Resolve-DscInput -Inputs $Parameter
            " --parameters $Parameter"
        }
        else
        {
            " --parameters-file $Parameter"
        }

        # Append the parameter to the command
        [void]$sb.Append($parameterInput)
    }

    [void]$sb.Append(" $Operation")

    $inputParameter = if (-not (Test-IsDscFilePath -Path $Inputs))
    {
        "--input $Inputs"
    }
    else
    {
        "--file $Inputs"
    }

    [void]$sb.Append(" $inputParameter")

    return $sb.ToString()
}
#EndRegion './Private/DSC/Confirm-DscConfigInput.ps1' 80
#Region './Private/DSC/Confirm-DscResourceInput.ps1' -1

function Confirm-DscResourceInput
{
    <#
    .SYNOPSIS
        Private function to confirm the input provided by the user.

    .DESCRIPTION
        The function Confirm-DscResourceInput confirms the resource input before invoking the DSC version 3 command-line utility.

    .PARAMETER Resource
        The resource (name) to be confirmed.

    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .PARAMETER Operation
        The operation to be performed. Supports 'get', 'set', 'test', and 'delete'.

    .EXAMPLE
        PS C:\> Confirm-DscResourceInput -Resource 'Microsoft.Windows/Registry' -Inputs '{"keyPath":"HKCU\\1\\2"' -Operation 'get'

        This example returns the string 'resource get --resource Microsoft.Windows/Registry --input {"keyPath":"HKCU\\1\\2"}'.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

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

        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [System.Object]
        $Inputs,

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

    )

    $ResourceInput = "resource $Operation --resource $Resource"

    if (Test-Path -LiteralPath $Inputs)
    {
        $ResourceInput += " --file $Inputs"
    }
    else
    {
        $ResourceInput += " --input $Inputs"
    }

    Write-Debug -Message "The resource input is: $ResourceInput"

    return $ResourceInput
}
#EndRegion './Private/DSC/Confirm-DscResourceInput.ps1' 62
#Region './Private/DSC/ConvertTo-DscObject.ps1' -1

function ConvertTo-DscObject
{
    <#
    .SYNOPSIS
        Convert DSC configuration documents to object

    .DESCRIPTION
        The function ConvertTo-DscObject 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:\> ConvertTo-DscObject -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/ConvertTo-DscObject.ps1' 354
#Region './Private/DSC/ConvertTo-PsDscInput.ps1' -1

function ConvertTo-PsDscInput
{
    <#
    .SYNOPSIS
        Converts the provided manifest to a DSC resource input format.

    .DESCRIPTION
        The function ConvertTo-PsDscInput converts the provided manifest to a Desired State Configuration resource input format. It supports extracting required properties only if specified.

    .PARAMETER Manifest
        The manifest to be converted to input.

    .PARAMETER RequiredOnly
        Switch to indicate if only required properties should be included.

    .EXAMPLE
        PS C:\> Get-PsDscManifest -Resource 'Microsoft/OSInfo'
        PS C:\> ConvertTo-PsDscInput -Manifest $manifest -RequiredOnly

        Returns:
        Name Value
        ---- -----
        family string
        codename string
        edition string
        version string
        architecture string
        $id string
        bitness string

    .NOTES
    For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter()]
        [AllowNull()]
        [System.Management.Automation.PSObject]
        $Manifest,

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

    begin
    {
        function Add-ResourceInput ($RequiredOnly, $InputObject)
        {
            if ($RequiredOnly)
            {
                foreach ($requiredValue in $InputObject.required)
                {
                    $resourceInput.Add($requiredValue, $null)
                }
            }
            else
            {
                $noteProperties = $InputObject.properties | Foreach-Object { Get-Member -MemberType NoteProperty -InputObject $_ }

                foreach ($noteProperty in $noteProperties)
                {
                    $resourceInput.Add($noteProperty.Name, $InputObject.properties.$($noteProperty.Name).type)
                }
            }
        }
    }

    process
    {
        # Create container bag
        $resourceInput = @{}

        if ($Manifest)
        {
            # Check if there is a schema embedded in the manifest
            if ($Manifest.schema.embedded)
            {
                Add-ResourceInput -RequiredOnly $RequiredOnly -InputObject $Manifest.schema.embedded
            }
            else
            {
                $processArgument = "resource schema --resource $($Manifest.type) --output-format json"

                $process = Get-ProcessObject -Argument $processArgument

                $result = Get-ProcessResult -Process $process

                if ($result.Output)
                {
                    $schema = $result.Output | ConvertFrom-Json

                    Add-ResourceInput -RequiredOnly $RequiredOnly -InputObject $schema
                }
            }
        }
    }

    end
    {
        return $resourceInput
    }
}
#EndRegion './Private/DSC/ConvertTo-PsDscInput.ps1' 106
#Region './Private/DSC/Get-CurrentDscExeVersion.ps1' -1

function Get-CurrentDscExeVersion
{
    <#
    .SYNOPSIS
        Get the current version of the installed DSC executable.

    .DESCRIPTION
        The function Get-CurrentDscExeVersion gets the current version of the installed DSC executable.

    .EXAMPLE
        PS C:\> Get-CurrentDscExeVersion

        Returns the current version of the installed DSC executable.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    if (Test-DscExe)
    {
        $dscVersion = (& dsc --version).Split(' ' )[1]
        return $dscVersion
    }
    else
    {
        return $null
    }
}
#EndRegion './Private/DSC/Get-CurrentDscExeVersion.ps1' 28
#Region './Private/DSC/Get-DscVersion.ps1' -1

function Get-DscVersion
{
    <#
    .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:\> Get-DscVersion -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 = Get-GithubReleaseVersion -Organization 'PowerShell' -Repository 'DSC' -ErrorAction Stop
            }
            catch
            {
                $script:availableDscVersions = @()
                Write-Verbose -Message "Failed to retrieve all available versions with error: $_"
            }
        }

        return $script:availableDscVersions
    }
}
#EndRegion './Private/DSC/Get-DscVersion.ps1' 46
#Region './Private/DSC/Get-PsDscBuiltinManifestResource.ps1' -1

function Get-PsDscBuiltinManifestResource
{
    <#
    .SYNOPSIS
        Get the builtin manifest for a resource from the specified manifest file.

    .DESCRIPTION
        The function Get-PsDscBuiltinManifestResource gets the builtin manifest for a resource from the specified manifest file.

    .PARAMETER ManifestFile
        The manifest file to read. This should be a valid manifest file starting with *.dsc.resource.json, *.dsc.resource.yml, or *.dsc.resource.yaml.

    .PARAMETER Resource
        The resource to get the builtin manifest for e.g. 'Microsoft.Windows/Registry'.

    .EXAMPLE
        PS C:\> Get-PsDscBuiltinManifestResource -ManifestFile 'C:\Program Files\dsc\registry.dsc.resource.json' -Resource 'Microsoft.Windows/Registry'

        This example gets the builtin manifest for the 'Microsoft.Windows/Registry' resource from the specified manifest file.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Management.Automation.PSCustomObject])]
    param
    (
        [Parameter()]
        [System.String[]]
        $ManifestFile,

        [Parameter()]
        [System.String]
        $Resource
    )

    $resourceManifest = foreach ($manifest in $ManifestFile)
    {
        Write-Debug -Message "Reading manifest file: $($manifest.Name)"
        $manifest = Get-Content -Path $manifest | ConvertFrom-Json

        if ($manifest.psobject.properties.name -notcontains 'kind' -and $manifest.type -eq $Resource)
        {
            $manifest
            break
        }
    }

    if (-not $resourceManifest)
    {
        Write-Warning -Message "No builtin manifest found for resource '$Resource'."
    }

    return $resourceManifest
}
#EndRegion './Private/DSC/Get-PsDscBuiltinManifestResource.ps1' 56
#Region './Private/DSC/Get-PsDscManifest.ps1' -1

function Get-PsDscManifest
{
    <#
    .SYNOPSIS
        Search for the manifest of a DSC resource.

    .DESCRIPTION
        The function Get-PsDscManifest searches for the manifest of a DSC resource. The steps it takes are:

        * Search for the manifest in the DSC executable directory.
        * Search for the manifest in the PowerShell adapters.

    .PARAMETER Resource
        The name of the DSC resource.

    .PARAMETER DscExe
        The path to the DSC version 3 executable 'dsc.exe'.

    .EXAMPLE
        PS C:\> Get-PsDscManifest -Resource 'Microsoft.Windows/Registry'

        This example searches for the manifest of the 'Microsoft.Windows/Registry' DSC resource.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

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

        [Parameter(Mandatory = $false)]
        [System.String]
        $DscExe = (Resolve-DscExe)
    )

    process
    {
        $rootPath = Split-Path -Path $DscExe -Parent

        $knownExtensions = @('*.dsc.resource.json', '*.dsc.resource.yml', '*.dsc.resource.yaml')

        $manifestFiles = Get-ChildItem -Path "$rootPath\*" -Include $knownExtensions

        # Get the standard manifest first if it exists
        $manifest = Get-PsDscBuiltinManifestResource -ManifestFile $manifestFiles -Resource $Resource

        if (-not $manifest)
        {
            # Go through the PowerShell adapters
            $manifest = Get-PsDscPowerShellManifestResource -Resource $Resource
        }

        return $manifest
    }

}
#EndRegion './Private/DSC/Get-PsDscManifest.ps1' 61
#Region './Private/DSC/Get-PsDscPowerShellManifestResource.ps1' -1

function Get-PsDscPowerShellManifestResource
{
    <#
    .SYNOPSIS
        Search for a PowerShell DSC resource manifest in the cache.

    .DESCRIPTION
        The function Get-PsDscPowerShellManifestResource searches for a PowerShell DSC resource manifest in the cache.

    .PARAMETER Resource
        The name of the resource to search for.

    .EXAMPLE
        PS C:\> Get-PsDscPowerShellManifestResource -Resource 'Microsoft.WinGet.DSC/WinGetPackage'

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

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

    # Check for PowerShell class-based first
    $cacheFilePath = Join-Path $env:LocalAppData "dsc\PSAdapterCache.json"

    if (-not (Test-Path $cacheFilePath -ErrorAction SilentlyContinue))
    {
        Write-Warning -Message "No PowerShell class-based manifest found. Please run 'Find-PsDscResource' to generate the cache."
        return
    }

    $poshManifest = Get-PsDscPowerShellResourceSchema -Resource $Resource -CacheFilePath $cacheFilePath

    if (-not $poshManifest)
    {
        Write-Warning -Message "No PowerShell class-based manifest found for resource '$Resource'."

        # Check for PowerShell script-based
        $cacheFilePath = Join-Path $env:LocalAppData "dsc\WindowsPSAdapterCache.json"

        if (-not (Test-Path $cacheFilePath -ErrorAction SilentlyContinue))
        {
            Write-Warning -Message "No PowerShell script-based manifest found. Please run 'Find-PsDscResource' to generate the cache."
            return
        }

        $poshManifest = Get-PsDscPowerShellResourceSchema -Resource $Resource -CacheFilePath $cacheFilePath

        if (-not $poshManifest)
        {
            Write-Warning -Message "No PowerShell script-based manifest found for resource '$Resource'."
            return
        }
    }

    return $poshManifest
}
#EndRegion './Private/DSC/Get-PsDscPowerShellManifestResource.ps1' 62
#Region './Private/DSC/Get-PsDscPowerShellResourceSchema.ps1' -1

function Get-PsDscPowerShellResourceSchema
{
    <#
    .SYNOPSIS
        Generates a custom schema for a PowerShell DSC resource based on the cache file and schema definition.

    .DESCRIPTION
        The function Get-PsDscPowerShellResourceSchema generates a custom schema for a PowerShell DSC resource based on the cache file and schema definition.

    .PARAMETER Resource
        The resource to look for in the cache file.

    .PARAMETER CacheFilePath
        The path to the cache file. The cache file is generated by running the 'Find-PsDscResource' function.

    .EXAMPLE
        PS C:\> Get-PsDscPowerShellResourceSchema -Resource 'Microsoft/OSInfo' -CacheFilePath "$env:LocalAppData\dsc\PSAdapterCache.json"

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Resource,

        [Parameter(Mandatory = $true)]
        [string]$CacheFilePath
    )

    try
    {
        $cache = Get-Content -Path $CacheFilePath | ConvertFrom-Json

        $cacheEntry = $cache.ResourceCache | Where-Object { $_.Type -eq $Resource }

        if ($cacheEntry)
        {
            $properties = $cacheEntry.DscResourceInfo.Properties | ForEach-Object {
                [PSCustomObject]@{
                    $_.Name = [PSCustomObject]@{
                        description = $_.Description
                        type        = $_.PropertyType
                    }
                }
            }

            return [PSCustomObject]@{
                '$schema'   = 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json'
                type        = $Resource
                description = $cacheEntry.DscResourceInfo.Description
                tags        = @('DSCResource')
                version     = $cacheEntry.DscResourceInfo.Version
                schema      = [PSCustomObject]@{
                    embedded = [PSCustomObject]@{
                        "`$schema" = "https://json-schema.org/draft/2020-12/schema"
                        title      = $cacheEntry.Type.Split("/")[1]
                        type       = 'object'
                        required   = @($cacheEntry.DscResourceInfo.Properties | Where-Object { $_.IsMandatory } | ForEach-Object {
                                $_.Name
                            })
                        properties = $properties
                    }
                }
            }
        }
    }
    catch
    {
        Write-Warning -Message "Failed to read the cache file. The error was: $_"
        return
    }
}
#EndRegion './Private/DSC/Get-PsDscPowerShellResourceSchema.ps1' 75
#Region './Private/DSC/Read-PsDscAdapterSchema.ps1' -1

function Read-PsDscAdapterSchema
{
    <#
    .SYNOPSIS
        Read the Desired State Configuration PowerShell adapter cache.

    .DESCRIPTION
        The function Read-PsDscAdapterSchema 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.

    .PARAMETER IsPwsh
        A switch parameter to determine if the current value from Windows PowerShell or PowerShell cache should be retrieved.

    .EXAMPLE
        PS C:\> Read-PsDscAdapterSchema

        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,

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

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

    process
    {
        $cacheFilePath = if ($IsWindows -or $IsPwsh.IsPresent)
        {
            # 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 -or -not $IsPwsh.IsPresent)
            {
                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 = @((Read-PsDscAdapterSchemaProperty @pParams))
            $resourceObject.resourceInput = $exampleCode

            $resourceObject
        }
    }
    end
    {
        Write-Verbose -Message ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        if ($ReturnTypeInfo)
        {
            $objectBag = $objectBag.Type
        }

        return $objectBag
    }
}
#EndRegion './Private/DSC/Read-PsDscAdapterSchema.ps1' 113
#Region './Private/DSC/Read-PsDscAdapterSchemaProperty.ps1' -1

function Read-PsDscAdapterSchemaProperty
{
    <#
    .SYNOPSIS
        Reads the schema properties of a DSC PowerShell adapter.

    .DESCRIPTION
        The function Read-PsDscAdapterSchemaProperty 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:\> Read-PsDscAdapterSchemaProperty -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((ConvertTo-HashString -HashTable $mandatory))
        $resourceInput.Add((ConvertTo-HashString -HashTable $inputObject))
    }
    else
    {
        $resourceInput.Add(($mandatory | ConvertTo-Json -Depth 10 -Compress))
        $resourceInput.Add(($inputObject | ConvertTo-Json -Depth 10 -Compress))
    }

    return $resourceInput
}
#EndRegion './Private/DSC/Read-PsDscAdapterSchemaProperty.ps1' 82
#Region './Private/DSC/Resolve-DscExe.ps1' -1

function Resolve-DscExe
{
    <#
    .SYNOPSIS
        Resolve the location of 'dsc.exe'.

    .DESCRIPTION
        The function Resolve-DscExe resolves the location of the Desired State Configuration version 3 executable 'dsc.exe'.

        It first checks if:

        - The script variable $script:dscExePath is set.
        - The common installation paths for Windows, Linux, and macOS.
        - Leverages the Get-Command cmdlet to search for 'dsc.exe' in the PATH.

    .EXAMPLE
        PS C:\> Resolve-DscExe

        This example resolves the location of 'dsc.exe' and returns the full path.

    .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
    ()

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

    $dscExePath = $null

    if ($IsWindows)
    {
        $commonPath = @("$env:ProgramFiles\dsc\dsc.exe", "$env:LOCALAPPDATA\dsc\dsc.exe")

        foreach ($path in $commonPath)
        {
            if (Test-Path $path)
            {
                Write-Verbose -Message "Found DSC executable in common path: $path."
                $dscExePath = $path
                break
            }
        }

        if (-not $dscExePath)
        {
            $dscExePath = (Get-Command -Name 'dsc.exe' -ErrorAction SilentlyContinue).Source
            if ($dscExePath)
            {
                Write-Verbose -Message "Found DSC executable in PATH: $dscExePath."
            }
        }
    }
    elseif ($IsLinux)
    {
        $dscExePath = Join-Path '/opt/' 'microsoft' 'dsc' 'dsc'
        if (-not (Test-Path $dscExePath))
        {
            $dscExePath = (Get-Command -Name 'dsc' -ErrorAction SilentlyContinue).Source
            if ($dscExePath)
            {
                Write-Verbose -Message "Found DSC executable in PATH: $dscExePath."
            }
        }
        else
        {
            Write-Verbose -Message "Found DSC executable in default installation path: $dscExePath."
        }
    }
    elseif ($IsMacOs)
    {
        $dscExePath = Join-Path '/usr/' 'local' 'microsoft' 'dsc' 'dsc'
        if (-not (Test-Path $dscExePath))
        {
            $dscExePath = (Get-Command -Name 'dsc' -ErrorAction SilentlyContinue).Source
            if ($dscExePath)
            {
                Write-Verbose -Message "Found DSC executable in PATH: $dscExePath."
            }
        }
        else
        {
            Write-Verbose -Message "Found DSC executable in default installation path: $dscExePath."
        }
    }

    if (-not [string]::IsNullOrEmpty($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."
    }
}
#EndRegion './Private/DSC/Resolve-DscExe.ps1' 106
#Region './Private/DSC/Resolve-DscInput.ps1' -1

function Resolve-DscInput
{
    <#
    .SYNOPSIS
        Resolve the input to a JSON string.

    .DESCRIPTION
        The function Resolve-DscInput resolves the input to a JSON string. The input can be a hashtable, JSON, YAML, or a file path (both JSON and YAML).

    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .EXAMPLE
        PS C:\> Resolve-DscInput -Inputs @{ keyPath = 'HKCU\1\2' }

        This example resolves the hashtable input to a JSON string.

    .EXAMPLE
        PS C:\> Resolve-DscInput -Inputs 'registry.json'

        This example resolves the JSON input to a JSON string.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Object]
        $Inputs
    )

    if (Test-IsDscFilePath -Path $Inputs)
    {
        return $Inputs
    }

    # Check for YAML, hash table, or JSON input
    $stringData = if ($Inputs -is [Hashtable])
    {
        Write-Debug -Message "The input is a hashtable."
        $Inputs
    }
    elseif (Test-Json $Inputs -ErrorAction SilentlyContinue)
    {
        Write-Debug -Message "The input is a JSON string."
        $Inputs | ConvertFrom-Json
    }
    elseif (Get-Command ConvertFrom-Yaml -ErrorAction SilentlyContinue)
    {
        Write-Debug -Message "The input is a YAML string."
        $Inputs | ConvertFrom-Yaml
    }
    else
    {
        throw "Failed to convert input to JSON. Make sure the input is a valid JSON, YAML, or a hashtable which can be converted to JSON."
    }

    $json = ($stringData | ConvertTo-Json -Depth 10 -Compress | ConvertTo-Json) -replace "\\\\", "\" | Out-String

    Write-Debug -Message "The resolved input is:"
    Write-Debug -Message $json
    return ($json -replace "`r`n", "")
}
#EndRegion './Private/DSC/Resolve-DscInput.ps1' 67
#Region './Private/DSC/Test-DscExe.ps1' -1

function Test-DscExe
{
    <#
    .SYNOPSIS
        Check if dsc.exe is installed.

    .DESCRIPTION
        Check if dsc.exe is installed. Returns either true or false.

    .EXAMPLE
        PS C:\> Test-DscExe

    .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/Test-DscExe.ps1' 30
#Region './Private/DSC/Test-IsDscFilePath.ps1' -1

function Test-IsDscFilePath
{
    <#
    .SYNOPSIS
        Tests if the given path is a valid DSC file path.

    .DESCRIPTION
        The function Test-IsDscFilePath tests if the given path is a valid DSC file path. The function returns true if the path is a valid DSC file path, otherwise false.

    .PARAMETER Path
        The path or input to test for.

    .EXAMPLE
        PS C:\> Test-IsDscFilePath -Path 'C:\path\to\file.json'

        This example tests if the given path is a valid DSC file path.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Path
    )

    return (Test-Path $Path -ErrorAction SilentlyContinue) -and ([System.IO.Path]::GetExtension($Path) -in @('.json', '.yaml', '.yml'))
}
#EndRegion './Private/DSC/Test-IsDscFilePath.ps1' 32
#Region './Private/GitHub/Get-GitHubReleaseVersion.ps1' -1

function Get-GithubReleaseVersion
{
    <#
    .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/Get-GitHubReleaseVersion.ps1' 57
#Region './Private/Process/Get-ProcessObject.ps1' -1

function Get-ProcessObject
{
    <#
    .SYNOPSIS
        Create a new process object.

    .DESCRIPTION
        The function Get-ProcessObject creates a new process object.

    .PARAMETER Argument
        The argument to provide to the process.

    .PARAMETER DscExe
        The path to the DSC version 3 executable 'dsc.exe'.

    .EXAMPLE
        PS C:\> Get-ProcessObject -Argument 'resource get --resource Microsoft.Windows/Registry'

        This example creates a new process object with the specified argument.

    .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]
        $Argument,

        [Parameter(Mandatory = $false)]
        [System.String]
        $DscExe = (Resolve-DscExe)
    )

    $process = [System.Diagnostics.Process]::new()

    $startParameters = @{
        FileName               = $DscExe
        UseShellExecute        = $false
        RedirectStandardOutput = $true
        RedirectStandardError  = $true
    }

    if (-not [string]::IsNullOrEmpty($Argument))
    {
        $startParameters['Arguments'] = $Argument
    }

    $startInfo = [System.Diagnostics.ProcessStartInfo]$startParameters

    $process.StartInfo = $startInfo

    return $process
}
#EndRegion './Private/Process/Get-ProcessObject.ps1' 58
#Region './Private/Process/Get-ProcessResult.ps1' -1

function Get-ProcessResult
{
    <#
    .SYNOPSIS
        Get the result of a process.

    .DESCRIPTION
        The function Get-ProcessResult gets the result of a process.

    .PARAMETER Process
        The process to get the result from.

    .EXAMPLE
        PS C:\> Get-ProcessResult -Process (Get-ProcessObject -Argument 'resource get --resource Microsoft.Windows/Registry')

        This example gets the result of the process created by Get-ProcessObject.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

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

    Write-Verbose -Message "Starting '$($Process.StartInfo.FileName)' with arguments '$($Process.StartInfo.Arguments)'"

    Write-Debug -Message "Process starting..."
    $startTime = Get-Date
    [void]$Process.Start()

    # Create a list to store the output
    $string = [System.Collections.Generic.List[string]]::new()
    if ($Process.StartInfo.RedirectStandardOutput)
    {
        while ($null -ne ($line = $Process.StandardOutput.ReadLine()))
        {
            if (-not [string]::IsNullOrEmpty($line))
            {
                $string.Add($line)
            }
        }
    }

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

    $Process.WaitForExit()
    $endTime = Get-Date
    $elapsedTime = $endTime - $startTime
    Write-Debug -Message "Process has exited. Elapsed time: $($elapsedTime.TotalSeconds) seconds."

    return [PSCustomObject]@{
        Executable = $Process.StartInfo.FileName
        Arguments  = $Process.StartInfo.Arguments
        ExitCode   = $Process.ExitCode
        Output     = $string
        Error      = $standardError
    }
}
#EndRegion './Private/Process/Get-ProcessResult.ps1' 68
#Region './Private/Utils/ConvertTo-HashString.ps1' -1

Function ConvertTo-HashString
{
    <#
    .SYNOPSIS
        Convert hashtable to string.

    .DESCRIPTION
        The function ConvertTo-HashString converts a hashtable to a string.

    .PARAMETER HashTable
        The hashtable to convert.

    .EXAMPLE
        PS C:\> ConvertTo-HashString -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/Utils/ConvertTo-HashString.ps1' 48
#Region './Public/ConvertTo-PsDscJson.ps1' -1

function ConvertTo-PsDscJson
{
    <#
    .SYNOPSIS
        Convert DSC Configuration (v1/v2) Document to JSON.

    .DESCRIPTION
        The function ConvertTo-PsDscJson 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-PsDscJson -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"
                                ]
                            }
                        }
                    ]
                }
            }
        }
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>


    [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 = Build-DscConfigDocument @PSBoundParameters
    }

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

function ConvertTo-PsDscYaml
{
    <#
    .SYNOPSIS
        Convert DSC Configuration (v1/v2) Document to YAML.

    .DESCRIPTION
        The function ConvertTo-PsDscYaml 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-PsDscYaml -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 = Build-DscConfigDocument @PSBoundParameters
    }

    end
    {
        Write-Verbose ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        if (-not (Get-Module -ListAvailable yayaml -ErrorAction SilentlyContinue) -or (Get-Module -ListAvailable powershell-yaml))
        {
            $inputObject = ConvertTo-Yaml -InputObject $configurationDocument -Depth 10
        }
        return $inputObject
    }
}
#EndRegion './Public/ConvertTo-PsDscYaml.ps1' 102
#Region './Public/Export-PsDscConfig.ps1' -1

function Export-PsDscConfig
{
  <#
  .SYNOPSIS
    Invokes the config export operation for DSC version 3 command-line utility.

  .DESCRIPTION
    The function Export-PsDscConfig invokes the config export operation on Desired State Configuration version 3 executable 'dsc.exe'.

  .PARAMETER Inputs
    The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .PARAMETER Parameter
    The parameter to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .EXAMPLE
    PS C:\> $configDoc = @{
      '$schema' = 'https://aka.ms/dsc/schemas/v3/bundled/config/document.json'
      resources = @(
        @{
          name = 'OSInfo'
          type = 'Microsoft/OSInfo'
          properties = @{}
        }
      )
    }

    PS C:\> Export-PsDscConfig -Inputs $configDoc

    This example exports the information on the 'Microsoft/OSInfo' resource.

  .NOTES
    For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
  #>

  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.Object]
    $Inputs,

    [Parameter()]
    [AllowNull()]
    [System.Object]
    $Parameter
  )

  $inputParameter = Resolve-DscInput -Inputs $Inputs

  $processArgument = Confirm-DscConfigInput -Inputs $inputParameter -Parameter $Parameter -Operation 'get'

  $process = Get-ProcessObject -Argument $processArgument

  $result = Get-ProcessResult -Process $process

  return $result
}
#EndRegion './Public/Export-PsDscConfig.ps1' 59
#Region './Public/Export-PsDscResource.ps1' -1

function Export-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke the export operation for DSC version 3 command-line utility.

    .DESCRIPTION
        The function Export-PsDscResource invokes the export operation on Desired State Configuration version 3 executable 'dsc.exe'.

    .PARAMETER Resource
        The resource (name) to be exported.

    .EXAMPLE
        PS C:\> Export-PsDscResource -Resource 'Microsoft/OSInfo'

        This example exports the 'Microsoft/OSInfo' DSC resource.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('ResourceName')]
        [ArgumentCompleter([ResourceCompleter])]
        [System.String]
        $Resource
    )

    $processArgument = "resource export --resource $Resource"

    $process = Get-ProcessObject -Argument $processArgument

    $result = Get-ProcessResult -Process $process

    return $result
}
#EndRegion './Public/Export-PsDscResource.ps1' 40
#Region './Public/Find-PsDscResource.ps1' -1

function Find-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke the list operation for DSC version 3 command-line utility.

    .DESCRIPTION
        The function Find-PsDscResource invokes the list operation on Desired State Configuration version 3 executable 'dsc.exe'.

    .PARAMETER AdapterName
        The adapter name to filter on. Supported values are 'Microsoft.DSC/PowerShell', 'Microsoft.Windows/WMI', and 'Microsoft.Windows/WindowsPowerShell'.

        The adapter name is optional.

    .PARAMETER Description
        The description to filter on. The description is optional.

    .PARAMETER Tag
        The tag to filter on. The tag is optional.

    .EXAMPLE
        PS C:\> Find-PsDscResource -Adapter 'Microsoft.Windows/WindowsPowerShell' -Description 'This is a test description' -Tag 'Test'

        This example finds the DSC resources with the specified adapter, description, and tag.

    .EXAMPLE
        PS C:\> Find-PsDscResource

        This example finds the DSC resources without any filters. It does not get any adapted resources.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $false)]
        [Alias('Adapter')]
        [System.String]
        $AdapterName,

        [Parameter(Mandatory = $false)]
        [System.String]
        $Description,

        [Parameter(Mandatory = $false)]
        [System.String]
        $Tag
    )

    $resourceInput = @('resource', 'list')

    $supportedAdapters = @(
        'Microsoft.DSC/PowerShell',
        'Microsoft.Windows/WMI',
        'Microsoft.Windows/WindowsPowerShell'
    )

    # TODO: Validate if we can fetch adapters from different location
    if (-not [string]::IsNullOrEmpty($AdapterName))
    {
        # TODO: We can return if PSAdapterCache.json is present without calling dsc.exe
        if ($AdapterName -in $supportedAdapters)
        {
            $resourceInput += "--adapter $AdapterName"
        }
        else
        {
            Write-Warning "The adapter '$AdapterName' is not supported. Supported adapters are: $($supportedAdapters -join ', ')"
        }
    }

    if (-not [string]::IsNullOrEmpty($Description))
    {
        $resourceInput += "--description $Description"
    }

    if (-not [string]::IsNullOrEmpty($Tag))
    {
        $resourceInput += "--tag $Tag"
    }

    $processArgument = $resourceInput -join ' '

    $process = Get-ProcessObject -Argument $processArgument

    $result = Get-ProcessResult -Process $process

    return $result
}
#EndRegion './Public/Find-PsDscResource.ps1' 92
#Region './Public/Get-PsDscConfig.ps1' -1

function Get-PsDscConfig
{
  <#
  .SYNOPSIS
    Invokes the config get operation for DSC version 3 command-line utility.

  .DESCRIPTION
    The function Get-PsDscConfig invokes the config get operation on Desired State Configuration version 3 executable 'dsc.exe'.

  .PARAMETER Inputs
    The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .PARAMETER Parameter
    The parameter to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .EXAMPLE
    PS C:\> $configDoc = @{
      '$schema' = 'https://aka.ms/dsc/schemas/v3/bundled/config/document.json'
      resources = @(
        @{
          name = 'Echo 1'
          type = 'Microsoft.DSC.Debug/Echo'
          properties = @{
            output = 'hello'
          }
        },
        @{
          name = 'Echo 2'
          type = 'Microsoft.DSC.Debug/Echo'
          properties = @{
            output = 'world'
          }
        }
      )
    }

    PS C:\> Get-PsDscConfig -Inputs $configDoc

    This example retrieves the DSC configuration with the specified inputs using a hashtable.

  .NOTES
    For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
  #>

  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.Object]
    $Inputs,

    [Parameter()]
    [AllowNull()]
    [System.Object]
    $Parameter
  )

  $inputParameter = Resolve-DscInput -Inputs $Inputs

  $processArgument = Confirm-DscConfigInput -Inputs $inputParameter -Parameter $Parameter -Operation 'get'

  $process = Get-ProcessObject -Argument $processArgument

  $result = Get-ProcessResult -Process $process

  return $result
}
#EndRegion './Public/Get-PsDscConfig.ps1' 68
#Region './Public/Get-PsDscResource.ps1' -1

function Get-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke the get operation for DSC version 3 command-line utility.

    .DESCRIPTION
        The function Get-PsDscResource invokes the get operation on Desired State Configuration version 3 executable 'dsc.exe'.

    .PARAMETER Resource
        The resource (name) to be retrieved.

    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .EXAMPLE
        PS C:\> Get-PsDscResource -Resource 'Microsoft.Windows/Registry' -Inputs @{ keyPath = 'HKCU\1\2' }

        This example retrieves the 'Microsoft.Windows/Registry' DSC resource with the specified inputs.

    .EXAMPLE
        PS C:\> $params = @{
            Resource = 'Microsoft.Windows/Registry'
            Inputs = 'registry.json'
        }
        PS C:\> Get-PsDscResource @params

        This example retrieves the 'Microsoft.Windows/Registry' DSC resource with the inputs provided in the 'registry.json' file.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('ResourceName')]
        [ArgumentCompleter([ResourceCompleter])]
        [System.String]
        $Resource,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Object]
        $Inputs
    )

    $resourceInput = Resolve-DscInput -Inputs $Inputs

    $processArgument = Confirm-DscResourceInput -Resource $Resource -Inputs $resourceInput -Operation 'get'

    $process = Get-ProcessObject -Argument $processArgument

    $result = Get-ProcessResult -Process $process

    return $result
}
#EndRegion './Public/Get-PsDscResource.ps1' 59
#Region './Public/Initialize-PsDscResourceInput.ps1' -1

function Initialize-PsDscResourceInput
{
    <#
    .SYNOPSIS
        Initializes a hashtable input for a DSC resource.

    .DESCRIPTION
        The Initialize-PsDscResourceInput function retrieves a DSC resource manifest and converts it into
        a hashtable format that can be used as input for DSC operations. It provides an easy way to get
        a template with all properties or just the required ones.

    .PARAMETER Resource
        The name of the DSC resource.

    .PARAMETER RequiredOnly
        When specified, only the required properties are included in the output.

    .EXAMPLE
        PS C:\> Initialize-PsDscResourceInput -Resource 'Microsoft.Windows/Registry'

        Returns a hashtable with all properties for the Registry resource.

    .EXAMPLE
        PS C:\> Initialize-PsDscResourceInput -Resource 'Microsoft.Windows/Registry' -RequiredOnly

        Returns a hashtable with only the required properties for the Registry resource.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('ResourceName')]
        [ArgumentCompleter([ResourceCompleter])]
        [System.String]
        $Resource,

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

    $manifest = Get-PsDscManifest -Resource $Resource

    $resourceInput = ConvertTo-PsDscInput -Manifest $manifest -RequiredOnly:$RequiredOnly

    return $resourceInput
}
#EndRegion './Public/Initialize-PsDscResourceInput.ps1' 52
#Region './Public/Install-DscExe.ps1' -1

function Install-DscExe
{
    <#
    .SYNOPSIS
        Install DSC executable.

    .DESCRIPTION
        The function Install-DscExe installs Desired State Configuration version 3 executable.

    .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.

    .EXAMPLE
        PS C:\> Install-DscExe

        Install the latest version of DSC

    .EXAMPLE
        PS C:\> Install-DscExe -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([VersionCompleter])]
        [System.String]
        $Version,

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

    $dscInstalled = Test-DscExe

    $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 = ('{0}/latest' -f $base)
    }

    $releases = Invoke-RestMethod -Uri $releaseUrl

    if ($IsWindows)
    {
        if ($Force.IsPresent -or -not $dscInstalled)
        {
            $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
            $exePath = Join-Path $env:LOCALAPPDATA 'dsc'

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

            # add to current process path
            $env:PATH += [System.IO.Path]::PathSeparator + $exePath

            # add to user path
            $currentPath = [System.Environment]::GetEnvironmentVariable("Path", "User")

            if ($currentPath -notlike "*$exePath*")
            {
                Write-Verbose -Message "Adding '$exePath' to user PATH"
                [System.Environment]::SetEnvironmentVariable("Path", "$currentPath;$exePath", "User")
            }

            # unblock the files
            Get-ChildItem -Path $exePath -Recurse | Unblock-File

            $dscVersion = Get-CurrentDscExeVersion

            if (-not [string]::IsNullOrEmpty($dscVersion))
            {
                Write-Verbose -Message "DSC successfully installed with version '$dscVersion'"

                Remove-Item $installerPath -ErrorAction SilentlyContinue -Force
                return $true
            }
            else
            {
                Write-Warning -Message "Failed to install DSC"
                return $false
            }
        }
        else
        {
            # TODO: When DSC is fully available in GitHub, compare versions and install if newer
            Write-Warning -Message "DSC is already installed. Use -Force to reinstall."
            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"

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

        curl -L -o $filePath $uri
        # Create the target folder where powershell will be placed
        sudo mkdir -p /usr/local/microsoft/dsc

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

        # Set execute permissions
        sudo chmod +x /usr/local/microsoft/dsc

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

        Get-ChildItem -Path /usr/local/microsoft/dsc -Recurse
        # Add to path
        $env:PATH += [System.IO.Path]::PathSeparator + "/usr/local/microsoft/dsc"
    }
}
#EndRegion './Public/Install-DscExe.ps1' 189
#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
#Region './Public/Remove-PsDscResource.ps1' -1

function Remove-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke the delete operation for DSC version 3 command-line utility.

    .DESCRIPTION
        The function Remove-PsDscResource invokes the delete operation on Desired State Configuration version 3 executable 'dsc.exe'.

    .PARAMETER Resource
        The resource (name) to be deleted.

    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .EXAMPLE
        PS C:\> Remove-PsDscResource -Resource 'Microsoft.Windows/Registry' -Inputs @{ keyPath = 'HKCU\1\2' }

        This example removes the 'Microsoft.Windows/Registry' DSC resource with the specified inputs.

    .EXAMPLE
        PS C:\> $params = @{
            Resource = 'Microsoft.Windows/Registry'
            Inputs = 'registry.json'
        }
        PS C:\> Remove-PsDscResource @params

        This example removes the 'Microsoft.Windows/Registry' DSC resource with the inputs provided in the 'registry.json' file.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('ResourceName')]
        [ArgumentCompleter([ResourceCompleter])]
        [System.String]
        $Resource,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Object]
        $Inputs
    )

    $resourceInput = Resolve-DscInput -Inputs $Inputs

    $processArgument = Confirm-DscResourceInput -Resource $Resource -Inputs $resourceInput -Operation 'delete'

    $process = Get-ProcessObject -Argument $processArgument

    if ($PSCmdlet.ShouldProcess("'$Resource' with '$resourceInput'" , "Remove"))
    {
        $result = Get-ProcessResult -Process $process
    }

    return $result
}
#EndRegion './Public/Remove-PsDscResource.ps1' 62
#Region './Public/Set-PsDscConfig.ps1' -1

function Set-PsDscConfig
{
  <#
  .SYNOPSIS
    Invokes the config set operation for DSC version 3 command-line utility.

  .DESCRIPTION
    The function Set-PsDscConfig invokes the config set operation on Desired State Configuration version 3 executable 'dsc.exe'.

  .PARAMETER Inputs
    The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .PARAMETER Parameter
    The parameter to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .EXAMPLE
    PS C:\> $configDoc = @{
      '$schema' = 'https://aka.ms/dsc/schemas/v3/bundled/config/document.json'
      resources = @(
        @{
          name = 'Echo 1'
          type = 'Microsoft.DSC.Debug/Echo'
          properties = @{
            output = 'hello'
          }
        },
        @{
          name = 'Echo 2'
          type = 'Microsoft.DSC.Debug/Echo'
          properties = @{
            output = 'world'
          }
        }
      )
    }

    PS C:\> Set-PsDscConfig -Inputs $configDoc

    This example retrieves the DSC configuration with the specified inputs using a hashtable.

  .NOTES
    For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
  #>

  [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
  param
  (
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.Object]
    $Inputs,

    [Parameter()]
    [AllowNull()]
    [System.String]
    $Parameter
  )

  $inputParameter = Resolve-DscInput -Inputs $Inputs

  $processArgument = Confirm-DscConfigInput -Inputs $inputParameter -Parameter $Parameter -Operation 'set'

  $process = Get-ProcessObject -Argument $processArgument

  if ($PSCmdlet.ShouldProcess("$Inputs" , "Set"))
  {
    $result = Get-ProcessResult -Process $process
  }

  return $result
}
#EndRegion './Public/Set-PsDscConfig.ps1' 71
#Region './Public/Set-PsDscResource.ps1' -1

function Set-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke the set operation for DSC version 3 command-line utility.

    .DESCRIPTION
        The function Set-PsDscResource invokes the set operation on Desired State Configuration version 3 executable 'dsc.exe'.

    .PARAMETER Resource
        The resource (name) to be set.

    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .EXAMPLE
        PS C:\> Set-PsDscResource -Resource 'Microsoft.Windows/Registry' -Inputs @{ keyPath = 'HKCU\1\2' }

        This example sets the 'Microsoft.Windows/Registry' DSC resource with the specified inputs.

    .EXAMPLE
        PS C:\> $params = @{
            Resource = 'Microsoft.Windows/Registry'
            Inputs = 'registry.json'
        }
        PS C:\> Set-PsDscResource @params

        This example sets the 'Microsoft.Windows/Registry' DSC resource with the inputs provided in the 'registry.json' file.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('ResourceName')]
        [ArgumentCompleter([ResourceCompleter])]
        [System.String]
        $Resource,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Object]
        $Inputs
    )

    $resourceInput = Resolve-DscInput -Inputs $Inputs

    $processArgument = Confirm-DscResourceInput -Resource $Resource -Inputs $resourceInput -Operation 'set'

    $process = Get-ProcessObject -Argument $processArgument

    if ($PSCmdlet.ShouldProcess("'$Resource' with '$resourceInput'" , "Set"))
    {
        $result = Get-ProcessResult -Process $process
    }

    return $result
}
#EndRegion './Public/Set-PsDscResource.ps1' 62
#Region './Public/Test-PsDscConfig.ps1' -1

function Test-PsDscConfig
{
  <#
  .SYNOPSIS
    Invokes the config test operation for DSC version 3 command-line utility.

  .DESCRIPTION
    The function Test-PsDscConfig invokes the config test operation on Desired State Configuration version 3 executable 'dsc.exe'.

  .PARAMETER Inputs
    The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .PARAMETER Parameter
    The parameter to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

  .EXAMPLE
    PS C:\> $configDoc = @{
      '$schema' = 'https://aka.ms/dsc/schemas/v3/bundled/config/document.json'
      resources = @(
        @{
          name = 'Echo 1'
          type = 'Microsoft.DSC.Debug/Echo'
          properties = @{
            output = 'hello'
          }
        },
        @{
          name = 'Echo 2'
          type = 'Microsoft.DSC.Debug/Echo'
          properties = @{
            output = 'world'
          }
        }
      )
    }

    PS C:\> Test-PsDscConfig -Inputs $configDoc

    This example tests the information on the 'Microsoft.DSC.Debug/Echo' resource.

  .NOTES
    For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
  #>

  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.Object]
    $Inputs,

    [Parameter()]
    [AllowNull()]
    [System.String]
    $Parameter
  )

  $inputParameter = Resolve-DscInput -Inputs $Inputs

  $processArgument = Confirm-DscConfigInput -Inputs $inputParameter -Parameter $Parameter -Operation 'test'

  $process = Get-ProcessObject -Argument $processArgument

  $result = Get-ProcessResult -Process $process

  return $result
}
#EndRegion './Public/Test-PsDscConfig.ps1' 68
#Region './Public/Test-PsDscResource.ps1' -1

function Test-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke the test operation for DSC version 3 command-line utility.

    .DESCRIPTION
        The function Test-PsDscResource invokes the test operation on Desired State Configuration version 3 executable 'dsc.exe'.

    .PARAMETER Resource
        The resource (name) to be tested.

    .PARAMETER Inputs
        The input to provide. Supports a hashtable of key-value pairs, JSON, YAML, or a file path (both JSON and YAML).

    .EXAMPLE
        PS C:\> Test-PsDscResource -Resource 'Microsoft.Windows/Registry' -Inputs @{ keyPath = 'HKCU\1\2' }

        This example tests the 'Microsoft.Windows/Registry' DSC resource with the specified inputs.

    .EXAMPLE
        PS C:\> $params = @{
            Resource = 'Microsoft.Windows/Registry'
            Inputs = 'registry.json'
        }
        PS C:\> Test-PsDscResource @params

        This example tests the 'Microsoft.Windows/Registry' DSC resource with the inputs provided in the 'registry.json' file.

    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('ResourceName')]
        [ArgumentCompleter([ResourceCompleter])]
        [System.String]
        $Resource,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Object]
        $Inputs
    )

    $resourceInput = Resolve-DscInput -Inputs $Inputs

    $processArgument = Confirm-DscResourceInput -Resource $Resource -Inputs $resourceInput -Operation 'test'

    $process = Get-ProcessObject -Argument $processArgument

    $result = Get-ProcessResult -Process $process

    return $result
}
#EndRegion './Public/Test-PsDscResource.ps1' 59