Search code examples
c#powershellpowershell-cmdletscriptblock

Custom PowerShell cmdlet with a ScriptBlock in a hashtable


I have a custom Powershell Cmdlet in C#, all works fine.

The one parameter is a HashTable. How can I use a ScriptBlock in that parameter? When I set the parameter to @{file={$_.Identity}}, I want to get a pipeline object with Identity property in the ProcessRecord method. How can I do that?

Now I simple convert the keys/values of the hash table to Dictionary<string, string>, but I want to get a pipelined object property (string).

Now I get an error that ScriptBlock can't convert to string.


Solution

  • You could use ForEach-Object for this:

    function Invoke-WithUnderScore {
      param(
        [Parameter(ValueFromPipeline)]
        [object[]]$InputObject,
        [scriptblock]$Property
      )
    
      process {
        $InputObject |ForEach-Object $Property
      }
    }
    

    Then use like:

    PS C:\> "Hello","World!","This is a longer string" |Invoke-WithUnderscore -Property {$_.Length}
    5
    6
    23
    

    Or in a C# cmdlet:

    [Cmdlet(VerbsCommon.Select, "Stuff")]
    public class SelectStuffCommand : PSCmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public object[] InputObject;
    
        [Parameter()]
        public Hashtable Property;
    
        private List<string> _files;
    
        protected override void ProcessRecord()
        {
            string fileValue = string.Empty;
            foreach (var obj in InputObject)
            {
                if (!Property.ContainsKey("file"))
                    continue;
    
                if (Property["file"] is ScriptBlock)
                {
                    using (PowerShell ps = PowerShell.Create(InitialSessionState.CreateDefault2()))
                    {
                        var result = ps.AddCommand("ForEach-Object").AddParameter("process", Property["file"]).Invoke(new[] { obj });
                        if (result.Count > 0)
                        {
                            fileValue = result[0].ToString();
                        }
                    }
                }
                else
                {
                    fileValue = Property["file"].ToString();
                }
    
                _files.Add(fileValue);
            }
        }
    
        protected override void EndProcessing()
        {
            // process _files
        }
    }