2

We want to store and manage PowerShell scripts in a database an execute them via C#. How can we discover the parameters of such a script before executing it? so we can set them to known values or prompt a user for values.

Some clarification:

We create a management system MS.

  1. An admin stores a PowerShell script in the MS database.
  2. Later a different admin selects this script from a list offered by the MS.
  3. MS discovers the parameters of the script.
  4. MS prompts the admin for values.
  5. MS executes the script with the parameters supplied.

        string testScript = @"
            {
                param(
                    [ValidateNotNullOrEmpty()]
                    [string]$Name
                )
                get-process $name
            ";
    
    Dictionary<string,object> DiscoverParameters()
    {
        using (PowerShell psi = PowerShell.Create())
        {
            psi.AddScript(testScript);
            var pars = new Dictionary<string,object>();
            //How do we get at the parameters
            return pars;
        }
    }
    
    void ExecuteScript(Dictionary<string,object> pars)
    {
        using (PowerShell psi = PowerShell.Create())
        {
            psi.AddScript(testScript);
            pars.ToList().ForEach(p => psi.AddParameter(p.Key, p.Value));
            Collection<PSObject> PSOutput = psi.Invoke();
            //...
        }
    }
    
Peter Meinl
  • 2,566
  • 25
  • 39

2 Answers2

3

You can use the PS parser, and access the parameter information via AST:

$scriptfile = '<full path to script file>'
$AST = [System.Management.Automation.Language.Parser]::ParseFile( $scriptfile,[ref]$null,[ref]$null)

$AST.ParamBlock.Parameters | ft
mjolinor
  • 66,130
  • 7
  • 114
  • 135
3

mjolinor is correct that using the PowerShell parser is probably the best way to get the parameters. That example is in PowerShell, below is an example in C#. I'm not quite sure what you are looking for with the parameters being Dictionary<string, object>. Here we just stick the names into the list although there is other info you could pull out like the static type.

using System.Management.Automation;
using System.Management.Automation.Language;

static void Main(string[] args)
{
    const string testScript = @"
    param(
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )
    get-process $name
";
    foreach(var parameter in GetScriptParameters(testScript))
    {
        Console.WriteLine(parameter);
    }
}

private static List<string> GetScriptParameters(string script)
{
    Token[] tokens;
    ParseError[] errors;
    var ast = Parser.ParseInput(script, out tokens, out errors);
    if (errors.Length != 0)
    {
        Console.WriteLine("Errors: {0}", errors.Length);
        foreach (var error in errors)
        {
            Console.WriteLine(error);
        }
        return null;
    }

    return ast.ParamBlock.Parameters.Select(p => p.Name.ToString()).ToList();
}
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122