Ejemplo n.º 1
0
        /// <summary>
        /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way.
        /// </summary>
        internal PowerShellManager CheckoutIdleWorker(StreamingMessage request, AzFunctionInfo functionInfo)
        {
            PowerShellManager psManager    = null;
            string            requestId    = request.RequestId;
            string            invocationId = request.InvocationRequest?.InvocationId;

            // If the pool has an idle one, just use it.
            if (!_pool.TryTake(out psManager))
            {
                // The pool doesn't have an idle one.
                if (_poolSize < _upperBound &&
                    Interlocked.Increment(ref _poolSize) <= _upperBound)
                {
                    // If the pool hasn't reached its bounded capacity yet, then
                    // we create a new item and return it.
                    var logger = new RpcLogger(_msgStream);
                    logger.SetContext(requestId, invocationId);
                    psManager = new PowerShellManager(logger);

                    RpcLogger.WriteSystemLog(string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, _poolSize.ToString()));
                }
                else
                {
                    // If the pool has reached its bounded capacity, then the thread
                    // should be blocked until an idle one becomes available.
                    psManager = _pool.Take();
                }
            }

            // Register the function with the Runspace before returning the idle PowerShellManager.
            FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo);
            psManager.Logger.SetContext(requestId, invocationId);
            return(psManager);
        }
        private void SetInputBindingParameterValues(
            AzFunctionInfo functionInfo,
            IEnumerable <ParameterBinding> inputData,
            DurableController durableController,
            Hashtable triggerMetadata,
            TraceContext traceContext)
        {
            foreach (var binding in inputData)
            {
                if (functionInfo.FuncParameters.TryGetValue(binding.Name, out var paramInfo))
                {
                    if (!durableController.TryGetInputBindingParameterValue(binding.Name, out var valueToUse))
                    {
                        var bindingInfo = functionInfo.InputBindings[binding.Name];
                        valueToUse = Utils.TransformInBindingValueAsNeeded(paramInfo, bindingInfo, binding.Data.ToObject());
                    }

                    _pwsh.AddParameter(binding.Name, valueToUse);
                }
            }

            // Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
            if (functionInfo.HasTriggerMetadataParam)
            {
                _pwsh.AddParameter(AzFunctionInfo.TriggerMetadata, triggerMetadata);
            }

            if (functionInfo.HasTraceContextParam)
            {
                _pwsh.AddParameter(AzFunctionInfo.TraceContext, traceContext);
            }
        }
        /// <summary>
        /// Helper method to set the output binding metadata for the function that is about to run.
        /// </summary>
        internal void RegisterFunctionMetadata(AzFunctionInfo functionInfo)
        {
            var outputBindings = functionInfo.OutputBindings;

            FunctionMetadata.OutputBindingCache.AddOrUpdate(_pwsh.Runspace.InstanceId,
                                                            outputBindings,
                                                            (key, value) => outputBindings);
        }
 private static Hashtable InvokeFunction(
     PowerShellManager powerShellManager,
     AzFunctionInfo functionInfo,
     Hashtable triggerMetadata = null,
     RetryContext retryContext = null)
 {
     return(powerShellManager.InvokeFunction(functionInfo, triggerMetadata, null, retryContext, s_testInputData, new FunctionInvocationPerformanceStopwatch()));
 }
        public Hashtable InvokeFunction(
            AzFunctionInfo functionInfo,
            Hashtable triggerMetadata,
            TraceContext traceContext,
            RetryContext retryContext,
            IList <ParameterBinding> inputData,
            FunctionInvocationPerformanceStopwatch stopwatch)
        {
            var outputBindings = FunctionMetadata.GetOutputBindingHashtable(_pwsh.Runspace.InstanceId);

            var durableController = new DurableController(functionInfo.DurableFunctionInfo, _pwsh);

            try
            {
                durableController.BeforeFunctionInvocation(inputData);

                AddEntryPointInvocationCommand(functionInfo);
                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.FunctionCodeReady);

                SetInputBindingParameterValues(functionInfo, inputData, durableController, triggerMetadata, traceContext, retryContext);
                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.InputBindingValuesReady);

                if (!durableController.ShouldSuppressPipelineTraces())
                {
                    _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Trace-PipelineObject");
                }

                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.InvokingFunctionCode);
                Logger.Log(isUserOnlyLog: false, LogLevel.Trace, CreateInvocationPerformanceReportMessage(functionInfo.FuncName, stopwatch));

                try
                {
                    return(durableController.TryInvokeOrchestrationFunction(out var result)
                                ? result
                                : InvokeNonOrchestrationFunction(durableController, outputBindings));
                }
                catch (RuntimeException e)
                {
                    ErrorAnalysisLogger.Log(Logger, e.ErrorRecord, isException: true);
                    Logger.Log(isUserOnlyLog: true, LogLevel.Error, GetFunctionExceptionMessage(e));
                    throw;
                }
                catch (OrchestrationFailureException e)
                {
                    if (e.InnerException is IContainsErrorRecord inner)
                    {
                        Logger.Log(isUserOnlyLog: true, LogLevel.Error, GetFunctionExceptionMessage(inner));
                    }
                    throw;
                }
            }
            finally
            {
                durableController.AfterFunctionInvocation();
                outputBindings.Clear();
                ResetRunspace();
            }
        }
        internal Hashtable InvokeOrchestrationFunction(
            AzFunctionInfo functionInfo,
            string contextParamName,
            OrchestrationContext context)
        {
            context.ActionEvent = _actionEvent;

            try
            {
                if (!functionInfo.FuncParameters.ContainsKey(contextParamName))
                {
                    // TODO: we should do more analysis in FunctionLoadRequest phase
                    throw new InvalidOperationException($"Orchestration function should define the parameter '-{contextParamName}'");
                }

                _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Set-FunctionInvocationContext")
                .AddParameter("OrchestrationContext", context)
                .InvokeAndClearCommands();

                _pwsh.AddCommand(string.IsNullOrEmpty(functionInfo.EntryPoint) ? functionInfo.ScriptPath : functionInfo.EntryPoint)
                .AddParameter(contextParamName, context);

                var outputBuffer = new PSDataCollection <object>();
                var asyncResult  = _pwsh.BeginInvoke <object, object>(input: null, output: outputBuffer);

                var waitHandles         = new[] { _actionEvent, asyncResult.AsyncWaitHandle };
                var signaledHandleIndex = WaitHandle.WaitAny(waitHandles);
                var signaledHandle      = waitHandles[signaledHandleIndex];

                OrchestrationMessage retResult = null;
                if (ReferenceEquals(signaledHandle, _actionEvent))
                {
                    // _actionEvent signaled
                    _pwsh.Stop();
                    retResult = new OrchestrationMessage(isDone: false, actions: context.Actions, output: null);
                }
                else
                {
                    // asyncResult.AsyncWaitHandle signaled
                    _pwsh.EndInvoke(asyncResult);
                    var result = CreateReturnValueFromFunctionOutput(outputBuffer);
                    retResult = new OrchestrationMessage(isDone: true, actions: context.Actions, output: result);
                }

                return(new Hashtable {
                    { AzFunctionInfo.DollarReturn, retResult }
                });
            }
            finally
            {
                _pwsh.Streams.ClearStreams();
                _pwsh.Commands.Clear();

                ClearInvocationContext();
                ResetRunspace();
            }
        }
        private void AddEntryPointInvocationCommand(AzFunctionInfo functionInfo)
        {
            if (string.IsNullOrEmpty(functionInfo.EntryPoint))
            {
                _pwsh.AddCommand(functionInfo.DeployedPSFuncName ?? functionInfo.ScriptPath);
            }
            else
            {
                // If an entry point is defined, we import the script module.
                _pwsh.AddCommand(Utils.ImportModuleCmdletInfo)
                .AddParameter("Name", functionInfo.ScriptPath)
                .InvokeAndClearCommands();

                _pwsh.AddCommand(functionInfo.EntryPoint);
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way.
        /// </summary>
        internal PowerShellManager CheckoutIdleWorker(StreamingMessage request, AzFunctionInfo functionInfo)
        {
            PowerShellManager psManager    = null;
            string            requestId    = request.RequestId;
            string            invocationId = request.InvocationRequest?.InvocationId;

            // If the pool has an idle one, just use it.
            if (!_pool.TryTake(out psManager))
            {
                // The pool doesn't have an idle one.
                if (_poolSize < UpperBound)
                {
                    int id = Interlocked.Increment(ref _poolSize);
                    if (id <= UpperBound)
                    {
                        // If the pool hasn't reached its bounded capacity yet, then
                        // we create a new item and return it.
                        var logger = CreateLoggerWithContext(requestId, invocationId);
                        psManager = new PowerShellManager(logger, id);

                        RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, id.ToString()));
                    }
                }

                if (psManager == null)
                {
                    var logger = CreateLoggerWithContext(requestId, invocationId);
                    logger.Log(isUserOnlyLog: true, LogLevel.Warning, string.Format(PowerShellWorkerStrings.FunctionQueuingRequest, functionInfo.FuncName));

                    // If the pool has reached its bounded capacity, then the thread
                    // should be blocked until an idle one becomes available.
                    psManager = _pool.Take();
                }
            }

            // Finish the initialization if not yet.
            // This applies only to the very first PowerShellManager instance, whose initialization was deferred.
            psManager.Initialize();

            // Register the function with the Runspace before returning the idle PowerShellManager.
            FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo);
            psManager.Logger.SetContext(requestId, invocationId);

            return(psManager);
        }
        static HelperModuleTests()
        {
            var funcDirectory   = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "TestScripts", "PowerShell");
            var rpcFuncMetadata = new RpcFunctionMetadata()
            {
                Name       = "TestFuncApp",
                Directory  = funcDirectory,
                ScriptFile = Path.Join(funcDirectory, "testBasicFunction.ps1"),
                EntryPoint = string.Empty,
                Bindings   =
                {
                    { "req",    new BindingInfo {
                          Direction = BindingInfo.Types.Direction.In, Type = "httpTrigger"
                      } },
                    { Response, new BindingInfo {
                          Direction = BindingInfo.Types.Direction.Out, Type = "http"
                      } },
                    { Queue,    new BindingInfo {
                          Direction = BindingInfo.Types.Direction.Out, Type = "queue"
                      } },
                    { Foo,      new BindingInfo {
                          Direction = BindingInfo.Types.Direction.Out, Type = "new"
                      } },
                    { Bar,      new BindingInfo {
                          Direction = BindingInfo.Types.Direction.Out, Type = "new"
                      } },
                    { Food,     new BindingInfo {
                          Direction = BindingInfo.Types.Direction.Out, Type = "new"
                      } }
                }
            };

            var funcLoadReq = new FunctionLoadRequest {
                FunctionId = "FunctionId", Metadata = rpcFuncMetadata
            };

            FunctionLoader.SetupWellKnownPaths(funcLoadReq);
            s_pwsh     = Utils.NewPwshInstance();
            s_funcInfo = new AzFunctionInfo(rpcFuncMetadata);
        }
        /// <summary>
        /// Execution a function fired by a trigger or an activity function scheduled by an orchestration.
        /// </summary>
        internal Hashtable InvokeFunction(
            AzFunctionInfo functionInfo,
            Hashtable triggerMetadata,
            IList <ParameterBinding> inputData)
        {
            string scriptPath = functionInfo.ScriptPath;
            string entryPoint = functionInfo.EntryPoint;
            string moduleName = null;

            try
            {
                // If an entry point is defined, we load the script as a module and invoke the function with that name.
                // We also need to fetch the ParameterMetadata to know what to pass in as arguments.
                var parameterMetadata = RetriveParameterMetadata(functionInfo, out moduleName);
                _pwsh.AddCommand(String.IsNullOrEmpty(entryPoint) ? scriptPath : entryPoint);

                // Set arguments for each input binding parameter
                foreach (ParameterBinding binding in inputData)
                {
                    if (parameterMetadata.ContainsKey(binding.Name))
                    {
                        _pwsh.AddParameter(binding.Name, binding.Data.ToObject());
                    }
                }

                // Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
                if (parameterMetadata.ContainsKey(AzFunctionInfo.TriggerMetadata))
                {
                    _logger.Log(LogLevel.Debug, "Parameter '-TriggerMetadata' found.");
                    _pwsh.AddParameter(AzFunctionInfo.TriggerMetadata, triggerMetadata);
                }

                Collection <object> pipelineItems = null;
                using (ExecutionTimer.Start(_logger, "Execution of the user's function completed."))
                {
                    pipelineItems = _pwsh.InvokeAndClearCommands <object>();
                }

                var result = _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Get-OutputBinding")
                             .AddParameter("Purge")
                             .InvokeAndClearCommands <Hashtable>()[0];

                if (pipelineItems != null && pipelineItems.Count > 0)
                {
                    // Log everything we received from the pipeline
                    foreach (var item in pipelineItems)
                    {
                        _logger.Log(LogLevel.Information, $"OUTPUT: {_pwsh.FormatObjectToString(item)}", isUserLog: true);
                    }

                    // TODO: See GitHub issue #82. We are not settled on how to handle the Azure Functions concept of the $returns Output Binding
                    // If we would like to support Option 1 from #82, uncomment the following code:
                    // object[] items = new object[pipelineItems.Count];
                    // pipelineItems.CopyTo(items, 0);
                    // result.Add(AzFunctionInfo.DollarReturn, items);

                    // If we would like to support Option 2 from #82, uncomment this line:
                    // result.Add(AzFunctionInfo.DollarReturn, pipelineItems[pipelineItems.Count - 1]);
                }

                return(result);
            }
            finally
            {
                ResetRunspace(moduleName);
            }
        }
        /// <summary>
        /// Execution a function fired by a trigger or an activity function scheduled by an orchestration.
        /// </summary>
        internal Hashtable InvokeFunction(
            AzFunctionInfo functionInfo,
            Hashtable triggerMetadata,
            TraceContext traceContext,
            IList <ParameterBinding> inputData,
            FunctionInvocationPerformanceStopwatch stopwatch)
        {
            string scriptPath = functionInfo.ScriptPath;
            string entryPoint = functionInfo.EntryPoint;
            var    hasSetInvocationContext = false;

            Hashtable outputBindings = FunctionMetadata.GetOutputBindingHashtable(_pwsh.Runspace.InstanceId);

            try
            {
                if (Utils.AreDurableFunctionsEnabled())
                {
                    // If the function has a output binding of the 'orchestrationClient' type, then we set the binding name
                    // in the module context for the 'Start-NewOrchestration' function to use.
                    if (!string.IsNullOrEmpty(functionInfo.OrchestrationClientBindingName))
                    {
                        _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Set-FunctionInvocationContext")
                        .AddParameter("OrchestrationStarter", functionInfo.OrchestrationClientBindingName)
                        .InvokeAndClearCommands();
                        hasSetInvocationContext = true;
                    }
                }

                if (string.IsNullOrEmpty(entryPoint))
                {
                    _pwsh.AddCommand(functionInfo.DeployedPSFuncName ?? scriptPath);
                }
                else
                {
                    // If an entry point is defined, we import the script module.
                    _pwsh.AddCommand(Utils.ImportModuleCmdletInfo)
                    .AddParameter("Name", scriptPath)
                    .InvokeAndClearCommands();

                    _pwsh.AddCommand(entryPoint);
                }

                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.FunctionCodeReady);

                // Set arguments for each input binding parameter
                foreach (ParameterBinding binding in inputData)
                {
                    string bindingName = binding.Name;
                    if (functionInfo.FuncParameters.TryGetValue(bindingName, out PSScriptParamInfo paramInfo))
                    {
                        var bindingInfo = functionInfo.InputBindings[bindingName];
                        var valueToUse  = Utils.TransformInBindingValueAsNeeded(paramInfo, bindingInfo, binding.Data.ToObject());
                        _pwsh.AddParameter(bindingName, valueToUse);
                    }
                }

                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.InputBindingValuesReady);

                // Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
                if (functionInfo.HasTriggerMetadataParam)
                {
                    _pwsh.AddParameter(AzFunctionInfo.TriggerMetadata, triggerMetadata);
                }
                if (functionInfo.HasTraceContextParam)
                {
                    _pwsh.AddParameter(AzFunctionInfo.TraceContext, traceContext);
                }

                _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Trace-PipelineObject");

                stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.InvokingFunctionCode);

                Logger.Log(isUserOnlyLog: false, LogLevel.Trace, CreateInvocationPerformanceReportMessage(functionInfo.FuncName, stopwatch));

                Collection <object> pipelineItems = null;

                try
                {
                    pipelineItems = _pwsh.InvokeAndClearCommands <object>();
                }
                catch (RuntimeException e)
                {
                    if (e.ErrorRecord.FullyQualifiedErrorId == "CommandNotFoundException")
                    {
                        Logger.Log(isUserOnlyLog: false, LogLevel.Warning, PowerShellWorkerStrings.CommandNotFoundException_Exception);
                    }

                    Logger.Log(isUserOnlyLog: true, LogLevel.Error, GetFunctionExceptionMessage(e));
                    throw;
                }

                Hashtable result = new Hashtable(outputBindings, StringComparer.OrdinalIgnoreCase);

                if (Utils.AreDurableFunctionsEnabled() && functionInfo.Type == AzFunctionType.ActivityFunction)
                {
                    var returnValue = CreateReturnValueFromFunctionOutput(pipelineItems);
                    result.Add(AzFunctionInfo.DollarReturn, returnValue);
                }

                return(result);
            }
            finally
            {
                if (hasSetInvocationContext)
                {
                    ClearInvocationContext();
                }

                outputBindings.Clear();
                ResetRunspace();
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Execution a function fired by a trigger or an activity function scheduled by an orchestration.
        /// </summary>
        internal Hashtable InvokeFunction(
            AzFunctionInfo functionInfo,
            Hashtable triggerMetadata,
            IList <ParameterBinding> inputData)
        {
            string scriptPath = functionInfo.ScriptPath;
            string entryPoint = functionInfo.EntryPoint;
            string moduleName = null;

            try
            {
                if (string.IsNullOrEmpty(entryPoint))
                {
                    _pwsh.AddCommand(scriptPath);
                }
                else
                {
                    // If an entry point is defined, we import the script module.
                    moduleName = Path.GetFileNameWithoutExtension(scriptPath);
                    _pwsh.AddCommand(Utils.ImportModuleCmdletInfo)
                    .AddParameter("Name", scriptPath)
                    .InvokeAndClearCommands();

                    _pwsh.AddCommand(entryPoint);
                }

                // Set arguments for each input binding parameter
                foreach (ParameterBinding binding in inputData)
                {
                    string bindingName = binding.Name;
                    if (functionInfo.FuncParameters.TryGetValue(bindingName, out PSScriptParamInfo paramInfo))
                    {
                        var bindingInfo = functionInfo.InputBindings[bindingName];
                        var valueToUse  = Utils.TransformInBindingValueAsNeeded(paramInfo, bindingInfo, binding.Data.ToObject());
                        _pwsh.AddParameter(bindingName, valueToUse);
                    }
                }

                // Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
                if (functionInfo.HasTriggerMetadataParam)
                {
                    _pwsh.AddParameter(AzFunctionInfo.TriggerMetadata, triggerMetadata);
                }

                Collection <object> pipelineItems = _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Trace-PipelineObject")
                                                    .InvokeAndClearCommands <object>();

                Hashtable result = _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Get-OutputBinding")
                                   .AddParameter("Purge", true)
                                   .InvokeAndClearCommands <Hashtable>()[0];

                /*
                 * TODO: See GitHub issue #82. We are not settled on how to handle the Azure Functions concept of the $returns Output Binding
                 * if (pipelineItems != null && pipelineItems.Count > 0)
                 * {
                 *  // If we would like to support Option 1 from #82, use the following 3 lines of code:
                 *  object[] items = new object[pipelineItems.Count];
                 *  pipelineItems.CopyTo(items, 0);
                 *  result.Add(AzFunctionInfo.DollarReturn, items);
                 *
                 *  // If we would like to support Option 2 from #82, use this line:
                 *  result.Add(AzFunctionInfo.DollarReturn, pipelineItems[pipelineItems.Count - 1]);
                 * }
                 */

                return(result);
            }
            finally
            {
                ResetRunspace(moduleName);
            }
        }
        /// <summary>
        /// Execution a function fired by a trigger or an activity function scheduled by an orchestration.
        /// </summary>
        internal Hashtable InvokeFunction(
            AzFunctionInfo functionInfo,
            Hashtable triggerMetadata,
            IList <ParameterBinding> inputData)
        {
            string scriptPath = functionInfo.ScriptPath;
            string entryPoint = functionInfo.EntryPoint;
            string moduleName = null;

            try
            {
                if (string.IsNullOrEmpty(entryPoint))
                {
                    _pwsh.AddCommand(scriptPath);
                }
                else
                {
                    // If an entry point is defined, we import the script module.
                    moduleName = Path.GetFileNameWithoutExtension(scriptPath);
                    _pwsh.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
                    .AddParameter("Name", scriptPath)
                    .InvokeAndClearCommands();

                    _pwsh.AddCommand(entryPoint);
                }

                // Set arguments for each input binding parameter
                foreach (ParameterBinding binding in inputData)
                {
                    if (functionInfo.FuncParameters.Contains(binding.Name))
                    {
                        _pwsh.AddParameter(binding.Name, binding.Data.ToObject());
                    }
                }

                // Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
                if (functionInfo.FuncParameters.Contains(AzFunctionInfo.TriggerMetadata))
                {
                    _logger.Log(LogLevel.Debug, "Parameter '-TriggerMetadata' found.");
                    _pwsh.AddParameter(AzFunctionInfo.TriggerMetadata, triggerMetadata);
                }

                Collection <object> pipelineItems = null;
                using (ExecutionTimer.Start(_logger, "Execution of the user's function completed."))
                {
                    pipelineItems = _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Trace-PipelineObject")
                                    .InvokeAndClearCommands <object>();
                }

                var result = _pwsh.AddCommand("Microsoft.Azure.Functions.PowerShellWorker\\Get-OutputBinding")
                             .AddParameter("Purge", true)
                             .InvokeAndClearCommands <Hashtable>()[0];

                /*
                 * TODO: See GitHub issue #82. We are not settled on how to handle the Azure Functions concept of the $returns Output Binding
                 * if (pipelineItems != null && pipelineItems.Count > 0)
                 * {
                 *  // If we would like to support Option 1 from #82, use the following 3 lines of code:
                 *  object[] items = new object[pipelineItems.Count];
                 *  pipelineItems.CopyTo(items, 0);
                 *  result.Add(AzFunctionInfo.DollarReturn, items);
                 *
                 *  // If we would like to support Option 2 from #82, use this line:
                 *  result.Add(AzFunctionInfo.DollarReturn, pipelineItems[pipelineItems.Count - 1]);
                 * }
                 */

                return(result);
            }
            finally
            {
                ResetRunspace(moduleName);
            }
        }