Public/Convert-DscResourceToCommand.ps1

<#
.SYNOPSIS
 
Creates commands from DSC resources.
#>

function Convert-DscResourceToCommand {
[CmdletBinding(DefaultParameterSetName = 'Module')]
[OutputType([System.Management.Automation.PSModuleInfo], ParameterSetName = 'Module')]
[OutputType([System.Management.Automation.PSModuleInfo], ParameterSetName = 'Module-Import')]
[OutputType([System.Management.Automation.PSObject], ParameterSetName = 'Module-AsCustomObject')]
[OutputType([String], ParameterSetName = 'AsString')]
param(
    [Parameter(
        Mandatory,
        ValueFromPipeline
    )]
    [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo[]]
    $Resource ,

    [Parameter()]
    [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()]
    [PSDefaultValue(Help = 'Module Defaults')]
    [HashTable]
    $CommandDefinition = (Get-DefaultDefinitions) ,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [SupportsWildcards()]
    [String[]]
    $IncludeVerb = '*' ,

    [Parameter()]
    [AllowEmptyCollection()]
    [AllowEmptyString()]
    [SupportsWildcards()]
    [String[]]
    $ExcludeVerb = 'Get' ,

    [Parameter()]
    [AllowEmptyCollection()]
    [AllowEmptyString()]
    [SupportsWildcards()]
    [ValidateScript( {
        [ResourcePropertyPattern]::new($_) -as [bool]
    } )]
    [String[]]
    $ExcludeProperty = '*:DependsOn' ,

    [Parameter()]
    [Switch]
    $ExcludeMandatory ,

    [Parameter()]
    [Alias('SupportsShouldProcess')]
    [Alias('ShouldProcess')]
    [Alias('SupportsWhatIf')]
    [Switch]
    $MockWhatIf ,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [String]
    $HardPrefix ,

    [Parameter()]
    [Alias('DefaultModule')]
    [AllowNull()]
    [AllowEmptyString()]
    [String]
    $DefaultResourceModuleName = 'PSDesiredStateConfiguration' ,

    [Parameter()]
    [Switch]
    $NoValidateSet = $AsCustomObject ,

    [Parameter(
        ParameterSetName = 'Module'
    )]
    [Parameter(
        ParameterSetName = 'Module-Import'
    )]
    [Parameter(
        ParameterSetName = 'Module-AsCustomObject'
    )]
    [AllowNull()]
    [AllowEmptyString()]
    [String]
    $ModuleName = 'Idempotion.Tincture' ,

    [Parameter(
        ParameterSetName = 'Module-Import'
    )]
    [Alias('SoftPrefix')]
    [ValidateNotNullOrEmpty()]
    [String]
    $Prefix ,

    [Parameter(
        ParameterSetName = 'Module-Import'
    )]
    [Switch]
    $NoClobber ,

    [Parameter(
        ParameterSetName = 'Module-Import'
    )]
    [Switch]
    $DisableNameChecking ,

    [Parameter(
        ParameterSetName = 'Module'
    )]
    [Parameter(
        ParameterSetName = 'Module-Import'
    )]
    [PSDefaultValue(Help = 'Always active when creating a module without importing it')]
    [Switch]
    $PassThru ,

    [Parameter(
        ParameterSetName = 'Module-Import' ,
        Mandatory
    )]
    [Switch]
    $Import ,

    [Parameter(
        ParameterSetName = 'Module-AsCustomObject' ,
        Mandatory
    )]
    [Switch]
    $AsCustomObject ,

    [Parameter(
        ParameterSetName = 'Module-Import'
    )]
    [Parameter(
        ParameterSetName = 'Module-AsCustomObject'
    )]
    [Switch]
    $Force ,

    [Parameter(
        ParameterSetName = 'AsString' ,
        Mandatory
    )]
    [Switch]
    $AsString
)

    Begin {
        try {
            $boundKeys = $PSBoundParameters.Keys.GetEnumerator().ForEach({$_})

            if ($DefaultResourceModuleName) {
                $DefaultModule = Get-Module -Name $DefaultResourceModuleName
            }

            $Definitions = Get-FilteredDefinitions -CommandDefinition $CommandDefinition -IncludeVerb $IncludeVerb -ExcludeVerb $ExcludeVerb

            $Functions = @()

            if ($AsCustomObject -and $boundKeys -notcontains 'CommandDefinition') {
                Write-Warning -Message "-AsCustomObject is only useful for very specific scenarios, and the default module definitions do not work correctly with it. This call will succeed, but the method calls will fail."
            }
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }

    Process {
        try {
            foreach ($ResourceDefinition in $Resource) {
                if (-not $ResourceDefinition.ModuleName -and $DefaultModule) {
                    $ResourceDefinition.Module = $DefaultModule
                }

                $paramsForParamBlock = @{
                    Resource = $ResourceDefinition
                }

                if ($NoValidateSet) {
                    $paramsForParamBlock.NoValidateSet = $NoValidateSet
                }
                if ($ExcludeProperty) {
                    $paramsForParamBlock.ExcludeProperty = $ExcludeProperty
                }
                if ($ExcludeMandatory) {
                    $paramsForParamBlock.ExcludeMandatory = $ExcludeMandatory
                }

                $ParamBlock = New-ParameterBlockFromResourceDefinition @paramsForParamBlock

                $DscModule = $ResourceDefinition.ModuleName

                $GeneratedFunctions = $Definitions.Verbs.GetEnumerator() | ForEach-Object -Process {
                    New-Object -TypeName PSObject -Property @{
                        Verb = $_.Key
                        CommandDefinition = $_.Value
                        ResourceName = $ResourceDefinition.Name
                        ParamBlock = $ParamBlock
                        DscModule = $DscModule
                        HardPrefix = $HardPrefix
                        ShouldProcess = $MockWhatIf
                        Snippets = $Definitions.Snippets
                    }
                } | New-FunctionFromDefinition

                if ($AsString) {
                    $GeneratedFunctions
                } else {
                    $Functions += $GeneratedFunctions
                }
            }
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }

    End {
        try {
            if (-not $AsString) {
                $nmoParams = @{
                    ScriptBlock = [ScriptBlock]::Create($Functions -join "`n")
                }
            
                if ($AsCustomObject) {
                    $nmoParams.AsCustomObject = $true
                }

                if ($ModuleName) {
                    $nmoParams.Name = $ModuleName
                }

                $ipmoParams = @{}

                $ipmoApplicableParamNames = @(
                     'DisableNameChecking'
                    ,'Force'
                    ,'PassThru'
                    ,'Prefix'
                    ,'NoClobber'
                    ,'AsCustomObject'
                )

                # Just a fancy way of not writing 6+ if statements

                Compare-Object -ReferenceObject $boundKeys -DifferenceObject $ipmoApplicableParamNames -ExcludeDifferent -IncludeEqual |
                    ForEach-Object -Process {
                        $paramName = $_.InputObject
                        $ipmoParams[$paramName] = $PSBoundParameters[$paramName]
                    }

                $newMod = New-Module @nmoParams -Verbose:$false

                if ($AsCustomObject) { # Just return it directly if we opted for a custom object
                    $newMod
                } else {
                    <#
 
                     New-Module automatically imports the module, but not in a way that makes it discoverable,
                     so it has to be removed first.
                     
                     If we want it imported, then we re-import it with Import-Module, which will:
 
                     A) bring it back into the session in a discoverable way (by using Get-Module)
                     B) allow -Prefix to be applied (not an option with New-Module)
 
                     If it shouldn't be imported, then we leave it removed and return it to the caller.
 
                    #>

                    $newMod | Remove-Module -Force -Verbose:$false

                    if ($Import) { # PassThru will be in $ipmoParams so the module will be returned if needed
                        if ($Force) {
                            Remove-Module -Name $newMod.Name -Force -ErrorAction Ignore -Verbose:$VerbosePreference
                        }
                        if ($Force -or -not (Get-Module -Name $newMod.Name -ErrorAction Ignore)) {
                            $newMod | Import-Module @ipmoParams -Global -Verbose:$VerbosePreference
                        }
                    } else {
                        $newMod  # module is always returned if it's not imported
                    }
                }
            } # -not $AsString # In the case of -AsString, the results were returned in the process block
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}