Skip to content

Export operation method cannot be called on a null-valued expression #503

@Gijsreyn

Description

@Gijsreyn

Prerequisites

  • Write a descriptive title.
  • Make sure you are able to repro it on the latest version
  • Search the existing issues.

Summary

Even thought that export is still expiremental, the following error is thrown when you execute export on a class-based DSC resource that doesn't have it implemented.

$document = @'
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
  - name: Use class PowerShell resources
    type: Microsoft.DSC/PowerShell
    properties:
      resources:
        - name: PowerShell 7 Preview
          type: Microsoft.WinGet.DSC/WinGetPackage
          properties:
            Id: Microsoft.PowerShell.Preview
            Ensure: "Present"
'@
dsc config export -d $document

Skimming through the code, I think the issue lays in the fact when the Invoke method is called which is empty:

# psDscAdapter.psm1

'Export' {
    $t = $dscResourceInstance.GetType()
    $method = $t.GetMethod('Export')
    $resultArray = $method.Invoke($null, $null)
    $addToActualState = $resultArray
}

Would it be good to include a new function and add it just before the operations are called in the switch statement:

function ValidateResourceOperationMethods {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $resourceObject,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Get', 'Set', 'Test', 'Export')]
        [string] $operation = 'Get'
    )

    $operations = ($resourceObject | Get-Member | Where-Object {$_.MemberType -eq 'Method'}).Name

    if (-not ($operations.Contains($operation))) {
        $errmsg = 'Can not find method on "' + $resourceObject.GetType().Name + '". Please make sure the DSC resource implements "' + $operation + '".'
        'ERROR: ' + $errmsg | Write-DscTrace
        exit 1
    }
}

function Invoke-DscOperation {
# truncated
        switch ([dscResourceType]$cachedDscResourceInfo.ImplementationDetail) {
  
            'ClassBased' {
                try {
                    # load powershell class from external module
                    $resource = GetTypeInstanceFromModule -modulename $cachedDscResourceInfo.ModuleName -classname $cachedDscResourceInfo.Name
                    $dscResourceInstance = $resource::New()

                    if ($DesiredState.properties) {
                        # set each property of $dscResourceInstance to the value of the property in the $desiredState INPUT object
                        $DesiredState.properties.psobject.properties | ForEach-Object -Process {
                            $dscResourceInstance.$($_.Name) = $_.Value
                        }
                    }
                    # Add this line before executing the operation
                    ValidateResourceOperationMethods -resourceObject $dscResourceInstance -operation $Operation

                    switch ($Operation) {
                        'Get' {
                            $Result = $dscResourceInstance.Get()
                            $addToActualState.properties = $Result
                        }
                        'Set' {
                            $dscResourceInstance.Set()
                        }
                        'Test' {
                            $Result = $dscResourceInstance.Test()
                            $addToActualState.properties = [psobject]@{'InDesiredState' = $Result } 
                        }
                        'Export' {
                            $t = $dscResourceInstance.GetType()
                            $method = $t.GetMethod('Export')
                            $resultArray = $method.Invoke($null, $null)
                            $addToActualState = $resultArray
                        }
                    }
                }
                catch {
                    
                    'ERROR: ' + $_.Exception.Message | Write-DscTrace
                    exit 1
                }
# truncated
}

Steps to reproduce

Run the code above if you have the Microsoft.WinGet.Dsc module installed.

Expected behavior

User friendly message why the method cannot be called on the resource.

Actual behavior

2024-07-28T09:18:07.115768Z DEBUG : dsc_lib::dscresources::command_resource: 812: Process 'pwsh' id 14588 : PSDesiredStateConfiguration module version:
2024-07-28T09:18:07.116249Z DEBUG : dsc_lib::dscresources::command_resource: 812: Process 'pwsh' id 14588 : ERROR: You cannot call a method on a null-valued expression.
2024-07-28T09:18:07.116965Z ERROR dsc::subcommand: 166: Error: Command: Resource 'pwsh' [Exit code 1] manifest description: Error

Error details

No response

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.3
PSEdition                      Core
GitCommitId                    7.4.3
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Version

dsc 3.0.0-preview.8

Visuals

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions