Functions/Invoke-POSHOriginNEW.ps1

function Invoke-POSHOriginNEW {
    [cmdletbinding()]
    param(
        [parameter(Mandatory,ValueFromPipeline)]
        [psobject[]]$Resource,

        [switch]$NoTranslate,

        [switch]$WhatIf,

        [switch]$PassThru
    )

    begin {

        # Start stopwatch
        $sw = [diagnostics.stopwatch]::StartNew()

        $results = @()

        # Used to track DSC resources that have been executed.
        # We'll use this so any dependent resources will only execute
        # if all of their dependencies have.
        # NOTE
        # This doesn't validate that the dependent service is in the desired state. Only that the "Set" function was executed
        $executedResources = @()

        if ($PSBoundParameters.ContainsKey('NoTranslate')) {
            Write-Verbose -Message "NoTranslate specified. No property translation will be attempted"
        }

        function Write-ResourceStatus {
            param (
                [string]$Resource,
                [string]$Name,
                [ValidateSet('Test', 'Get', 'Set')]
                [string]$State,
                [switch]$Inner,
                [switch]$Complete,
                [string]$Message
            )
            $cmd = {
                if (-Not $PSBoundParameters.ContainsKey('Inner')) {
                    switch ($State) {
                        'Test' {
                            Write-Host -Object "Testing resource "  -ForegroundColor Cyan -NoNewLine
                        }
                        'Get' {
                            Write-Host -Object "Getting resource "  -ForegroundColor Cyan -NoNewLine
                        }
                        'Set' {
                            Write-Host -Object "Setting resource "  -ForegroundColor Cyan -NoNewLine
                        }
                    }
                    Write-Host -Object "$Resource" -ForegroundColor Magenta -NoNewLine
                    Write-Host -Object '-' -ForegroundColor Gray -NoNewLine
                    Write-Host -Object $Name -ForegroundColor Green
                } else {
                    if (-Not $PSBoundParameters.ContainsKey('Complete')) {
                        Write-Host -Object " - $Message" -ForegroundColor Green
                    } else {

                        # Get the true/false and time result
                        $r = ($Message -split ' ')[0].Trim()
                        $time = ($message -split 'in')[1].Trim()
                        Write-Host -Object "Tested: " -ForegroundColor Cyan -NoNewline
                        if ($r -eq 'True') {
                            Write-Host -Object "[$r]" -ForegroundColor Green -NoNewline
                        } else {
                            Write-Host -Object "[$r]" -ForegroundColor Red -NoNewline
                        }
                        Write-Host -Object " in " -ForegroundColor Cyan -NoNewline
                        Write-Host -Object "$time" -ForegroundColor Green
                    }
                }
            }

            Invoke-Command -ScriptBlock $cmd
        }

        function Parse-DSCVerboseOutput([string]$line) {
            # Write line to log file
            Out-File -Encoding utf8 -Append -FilePath $outputFile -Inputobject $_

            # Try and extract the information we want from the line
            $line = $line | select-string -Pattern '^.*?:'
            $msg = $null
            if ($line) {
                $action = $type = $resName = $null
                #Write-Verbose $line

                $machine = ($line -split ']: ')[0].TrimStart(1,'[')
                $type = $resName = $null
                $message = [string]::Empty
                if ($line -match 'LCM:\s\s\[\s') {
                    $action = ($line -split '(LCM:\s\s\[)(\s)(.*?\s)(\s*.*?\s)')[3].Trim()
                    $type = ($line -split '(LCM:\s\s\[)(\s)(.*?\s)(\s*.*?\s)')[4].Trim()

                    #$action = ($line -split 'LCM:\s\s\[\s')[1].Split(' ')[0]
                    #$type = (($line -split 'LCM:\s\s\[\s')[1] -Split ']')[0].Split(' ')[2]
                    #$type = ((($line -split 'LCM:\s\s\[\s')[1] -Split ']')[0] -split '\s.*')[1]
                    if ($line -match '\[\[') {
                        $resName = ($line -split '\[\[')[1].Split(']')[0]
                    }
                }
                if ($line -match 'DirectResourceAccess\]') {
                    $message = ($line -split 'DirectResourceAccess\]')[1].Trim()
                } else {
                    $message = ($line -split 'LCM:\s\s\[\sEnd\s\s\s\sSet\s\s\s\s\s\s]')[1].Trim()
                }

                $msg = [pscustomobject]@{
                    machine = $machine
                    action = $action
                    type = $type
                    resource = $resName
                    message = $message
                }
                return $msg
                #Write-Host ($msg | ft -AutoSize | out-string)
            }
        }
    }

    process {
        # Temporarilly disable the PowerShell progress bar
        $oldProgPref = $global:ProgressPreference
        $global:ProgressPreference = 'SilentlyContinue'

        foreach ($item in $Resource) {

            $result = "" | Select Resource, InDesiredState

            # Derive the resource type and module from the resource properties
            # and try to find the DSC resource
            $module = $item.Resource.Split(':')[0]
            $resource = $item.Resource.Split(':')[1]
            $dscResource = _GetDscResource -module $module -Resource $resource

            if ($dscResource) {

                # Construct resource parameters
                if ($null -eq $item.Options.Ensure) {
                    $item.Options | Add-Member -Type NoteProperty -Name 'Ensure' -Value 'Present'
                }

                # Our params and hash to be splatted to Invoke-DscResource
                $params = @{}
                #$hash = @{}
                $hash = _GetDscResourcePropertyHash -DSCResource $dscResource -Resource $item -NoTranslate ($PSBoundParameters.ContainsKey('NoTranslate'))

                <#
                # Test for 'Invoke.ps1' script in DSC resource module and optionally use it to translate our options into what the
                # DSC resource expects.
                # If there is no 'Invoke.ps1' script or we specified '-NoTranslate' then pass the resource object directly to the DSC resource
                # without any translation. This requires that the correct property names are specificed in the configurations file
                # as they will be passed directly to Invoke-DscResource.
                $invokePath = Join-Path -Path $dscResource.ParentPath -ChildPath 'Invoke.ps1'
                if (Test-Path -Path $invokePath) {
                    if (-Not $PSBoundParameters.ContainsKey('NoTranslate')) {
                        # Use the 'Invoke.ps1' script to translate our options into what the DSC resource expects.
                        Write-Debug -Message "Calling $invokePath to translate properties"
                        $hash = & $invokePath -Options $item -Direct:$true
                    } else {
                        # We are intentially not using the 'Invoke.ps1' script and instead directly passing the object on
                        $propNames = $item.options | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | Where-Object {$_ -ne 'DependsOn'}
                        $propNames | ForEach-Object {
                            $hash.Add($_, $item.Options.$_)
                        }
                        # We have to stip out any properties from the POSHOrigin resource object that the DSC resource does not expect
                        $dscResourceProperties = $dscResource.Properties | Select-Object -ExpandProperty Name
                        $hashProperties = $hash.GetEnumerator() | Select-Object -ExpandProperty Name
                        foreach ($hashProperty in $hashProperties) {
                            if ($hashProperty -inotin $dscResourceProperties) {
                                $hash.remove($hashProperty)
                            }
                        }
                    }
                } else {
                    #throw "$invokePath not found in DSC module so no property translation could be made. Try using the -NoTranslate switch instead."
                    # There is no 'Invoke.ps1' script we we'll just pass on the properties directly to the DSC resource
                    # without any translation
                    $propNames = $item.options | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
                    $propNames | ForEach-Object {
                        $hash.Add($_, $item.Options.$_)
                    }
                    # We have to stip out any properties from the POSHOrigin resource object that the DSC resource does not expect
                    $dscResourceProperties = $dscResource.Properties | Select-Object -ExpandProperty Name
                    $hashProperties = $hash.GetEnumerator() | Select-Object -ExpandProperty Name
                    foreach ($hashProperty in $hashProperties) {
                        if ($hashProperty -inotin $dscResourceProperties) {
                            $hash.remove($hashProperty)
                        }
                    }
                }
                #>


                $params = @{
                    Name = $dscResource.Name
                    ModuleName = $dscResource.ModuleName
                    Property = $hash
                }

                #Write-Host ($params.Property | format-list * | Out-String)
                #Write-Host $hash.GuestCredentials.Username
                
                $outputFile = 'C:\temp\runlog.log'
                if (-not (Test-Path -Path $outputFile)) {
                    New-Item -Path $outputFile -Type File -Force
                }


                if ($PSBoundParameters.ContainsKey('WhatIf')) {
                    # Just test the resource
                    Write-ResourceStatus -Resource $dscResource.Name -Name $item.Name -State Test
                    $testResult = $null
                    $testResult = Invoke-DscResource -Method Test @params -Verbose:$VerbosePreference 4>&1 | foreach {
                        $msg = Parse-DSCVerboseOutput -line $_
                        if ($msg) {
                            if (($msg.message -ne [string]::Empty) -and ($msg.action -ne 'end')) {
                                Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message
                            }
                            if ($msg.action -eq 'end' -and $msg.type -eq 'test') {
                                Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message -Complete
                            }
                        }
                    }

                    if ($PSBoundParameters.ContainsKey('PassThru')) {
                        $result = "" | Select Resource, InDesiredState
                        Write-ResourceStatus -Resource $dscResource.Name -Name $item.Name -State Get

                        $getResult = $null
                        $getResult = Invoke-DscResource -Method Get @params -Verbose:$VerbosePreference 4>&1 | foreach {
                            $msg = Parse-DSCVerboseOutput -line $_
                            if ($msg) {
                                if (($msg.message -ne [string]::Empty) -and ($msg.action -ne 'end')) {
                                    Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message
                                }
                                if ($msg.action -eq 'end' -and $msg.type -eq 'test') {
                                    Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message -Complete
                                }
                            }
                        }
                        $result.Resource = $getResult
                        $result.InDesiredState = $testResult.InDesiredState
                        $results += $result
                    }
                } else {

                    # Test if this resource has any dependencies and only execute if those have been met.
                    $continue = $true
                    $dependenciesExist = @(($item.DependsOn).Count -gt 0)
                    if ($dependenciesExist) {
                        if ($dependency -inotin $executedResources.Keys) {
                            $continue = $false
                        }
                    } else {
                        $continue = $true
                    }

                    # All dependencies met?
                    if ($continue) {
                        # Test and invoke the resource
                        $testResult = $null
                        Write-ResourceStatus -Resource $dscResource.Name -Name $item.Name -State Test
                        $testResult = Invoke-DscResource -Method Test @params -Verbose:$VerbosePreference -InformationAction $InformationPreference 4>&1 | foreach {
                            $msg = Parse-DSCVerboseOutput -line $_
                            if ($msg) {
                                if (($msg.message -ne [string]::Empty) -and ($msg.action -ne 'end')) {
                                    Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message
                                }
                                if ($msg.action -eq 'end' -and $msg.type -eq 'test') {
                                    Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message -Complete
                                }
                            }
                        }
                        if (-Not $testResult.InDesiredState) {
                            Write-ResourceStatus -Resource $dscResource.Name -Name $item.Name -State Set
                            try {
                                $setResult = Invoke-DscResource -Method Set @params -Verbose:$VerbosePreference -InformationAction $InformationPreference 4>&1 | foreach {
                                    $msg = Parse-DSCVerboseOutput -line $_
                                    if ($msg) {
                                        if (($msg.message -ne [string]::Empty) -and ($msg.action -ne 'end')) {
                                            Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message
                                        }
                                        if ($msg.action -eq 'end' -and $msg.type -eq 'test') {
                                            Write-ResourceStatus -Resource $msg.resource -Name $item.Name -Inner -Message $msg.message -Complete
                                        }
                                    }
                                }
                            } catch {
                                Write-Error -Message 'There was a problem setting the resource'
                                Write-Error -Message "$($_.InvocationInfo.ScriptName)($($_.InvocationInfo.ScriptLineNumber)): $($_.InvocationInfo.Line)"
                                write-Error $_
                            }
                        }
                        # Track the resource as 'executed' for dependent resources
                        $executedResources += $item.FullName
                    } else {
                        Write-Error -Message "Dependencies have not been met for resource $($item.FullName). This resource will not be invoked."
                    }

                    if ($PSBoundParameters.ContainsKey('PassThru')) {
                        Write-ResourceStatus -Resource $dscResource.Name -Name $item.Name -State Test
                        $testResult = $null
                        $testResult = Invoke-DscResource -Method Test @params -Verbose:$VerbosePreference -InformationAction $InformationPreference
                        
                        Write-ResourceStatus -Resource $dscResource.Name -Name $item.Name -State Get
                        $getResult = $null
                        $getResult = Invoke-DscResource -Method Get @params -Verbose:$VerbosePreference -InformationAction $InformationPreference

                        $result.Resource = $getResult
                        $result.InDesiredState = $testResult.InDesiredState
                        $results += $result
                    }
                }
            } else {
                Write-Error -Message "Unable to find DSC resource: $($item.Resource)"
            }
            Write-Host -Object "`n"
        }
    }

    end {
        # Reset the progress bar preference
        $global:ProgressPreference = $oldProgPref

        if ($PSBoundParameters.ContainsKey('PassThru')) {
            $results
        }

        # Stop stopwatch
        Write-Verbose -Message "Command finished in $($sw.elapsed.seconds) seconds"
        $sw.stop()
    }
}