Support of script files and params

Jul 28, 2009 at 6:31 PM

Hello.

Great job on the library. I just implemented it in a Team Build to sign PowerShell scripts. It took me a while to realize the difference between specifying a script name supplying actual script. The latter does not support params, for instance. My custom build target specifies the script name that is used to sign other scripts. Therefore, I had to tweak the library a bit to support specifying the name. I figured I'd pass the code along in case you wanted to (more elgantly, I'm sure!) integrate it into the product. The key differences are that if a filename is being used, the Arguments property goes into the command's parameters collection. I also broke out the global variable initialization, although I haven't tested any of that to know if it's working.

        private bool FilenameSpecified
        {
            get
            {
                if (this.ScriptBlock != null && !string.IsNullOrEmpty(this.ScriptBlock.ItemSpec))
                    return false; // If a script block was specified use it.
                else if (!string.IsNullOrEmpty(this.ScriptFile))
                    return true;
                else
                    return false;
            }
        }

        public override bool Execute()
        {
            try
            {
                // Create the runspace and stuff.
                Runspace runspace = RunspaceFactory.CreateRunspace( this.host );
                Pipeline pipeline = runspace.CreatePipeline();

            
                this.SetUpOutputStreams( pipeline );

                runspace.Open();

                // Set up global variables.
                this.FillVariables(runspace);

                // Add our script to the pipeline.
                Command command = null;
                if (FilenameSpecified)
                {
                    command = new Command(this.ScriptFile, false);
                    
                    // Use the Arguments property to set the script Parameters
                    FillNamedParameters(command);
                }
                else
                {
                    command = new Command(this.ScriptBlock.ItemSpec.Trim(), true);

                    // Use the Arguments property to set variables in the script
                    this.FillNamedArguments(runspace);
                }

                pipeline.Commands.Add( command );

                // Run the script
                Collection<PSObject> psOutput = pipeline.Invoke();

                // Did it complete ok?
                PipelineStateInfo info = pipeline.PipelineStateInfo;
                if ( info.State != PipelineState.Completed )
                {
                    this.Log.LogErrorFromException( info.Reason, false );
                    return false;
                }

                var errors = pipeline.Error.ReadToEnd();
                Console.WriteLine( errors.Count );

                // return the output items as strings.
                if ( psOutput != null )
                {
                    PSObject[] outputObjects = new PSObject[psOutput.Count];
                    psOutput.CopyTo(outputObjects, 0);

                    this.Output = Array.ConvertAll<PSObject, ITaskItem>( outputObjects,
                                                                         psObject =>
                                                                         psObject != null
                                                                             ? new TaskItem(psObject.ToString())
                                                                             : new TaskItem(String.Empty));

                    return true;
                }
                return false;
            }
            catch ( Exception ex )
            {
                this.Log.LogErrorFromException( ex, false );
                throw;
            }

        }


        /// <summary>
        /// Sets named arguments to the task on the form "var=value".
        /// </summary>
        /// <param name="runspace"></param>
        private void FillNamedArguments(Runspace runspace)
        {
            if ( this.Arguments != null )
            {
                foreach (string argument in Arguments)
                {
                    int splitIdx = argument.IndexOf('=');
                    if (splitIdx <= 0)
                    {
                        throw new ArgumentException(String.Format("Invalid syntax for argument: {0}\r\nUse \"varname=value\"", argument));
                    }

                    string name = argument.Substring(0, splitIdx);
                    string value = argument.Substring(splitIdx + 1);

                    LogEvent(string.Format("Parsed variable '{0}' with value '{1}'", name, value));

                    runspace.SessionStateProxy.SetVariable(name, value);
                }
            }
        }
Coordinator
Aug 3, 2009 at 7:27 PM

Thanks for the code (although you omitted certain, uhm, important parts of it ;-). Your code formed the basis of changeset 26515 (http://powershellmsbuild.codeplex.com/SourceControl/ListDownloadableCommits.aspx), although I made some changes to it and wrote a unit test.

 

Perhaps it's time to make another release?

Aug 3, 2009 at 7:32 PM

Sorry. I didn't know if you'd even be interested. I figured I would just plant the bug in your ear and let you run with it.

Let me know if you need anything from me.