7
7
using System . Collections ;
8
8
using System . Collections . Generic ;
9
9
using System . Collections . ObjectModel ;
10
- using System . Text ;
10
+ using System . IO ;
11
11
12
12
using Microsoft . Azure . Functions . PowerShellWorker . Utility ;
13
13
using Microsoft . Azure . WebJobs . Script . Grpc . Messages ;
@@ -20,33 +20,16 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell
20
20
21
21
internal class PowerShellManager
22
22
{
23
- // This script handles when the user adds something to the pipeline.
24
- // It logs the item that comes and stores it as the $return out binding.
25
- // The last item stored as $return will be returned to the function host.
23
+ private const string _TriggerMetadataParameterName = "TriggerMetadata" ;
26
24
27
- readonly static string s_LogAndSetReturnValueScript = @"
28
- param([Parameter(ValueFromPipeline=$true)]$return)
25
+ private RpcLogger _logger ;
26
+ private PowerShell _pwsh ;
29
27
30
- Write-Information $return
31
-
32
- Set-Variable -Name '$return' -Value $return -Scope global
33
- " ;
34
-
35
- readonly static string s_SetExecutionPolicyOnWindowsScript = @"
36
- if ($IsWindows)
37
- {
38
- Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process
39
- }
40
- " ;
41
-
42
- readonly static string s_TriggerMetadataParameterName = "TriggerMetadata" ;
43
-
44
- RpcLogger _logger ;
45
- PowerShell _pwsh ;
46
-
47
- PowerShellManager ( RpcLogger logger )
28
+ internal PowerShellManager ( RpcLogger logger )
48
29
{
49
- _pwsh = PowerShell . Create ( InitialSessionState . CreateDefault ( ) ) ;
30
+ var initialSessionState = InitialSessionState . CreateDefault ( ) ;
31
+ initialSessionState . ExecutionPolicy = Microsoft . PowerShell . ExecutionPolicy . Unrestricted ;
32
+ _pwsh = PowerShell . Create ( initialSessionState ) ;
50
33
_logger = logger ;
51
34
52
35
// Setup Stream event listeners
@@ -59,64 +42,17 @@ internal class PowerShellManager
59
42
_pwsh . Streams . Warning . DataAdding += streamHandler . WarningDataAdding ;
60
43
}
61
44
62
- public static PowerShellManager Create ( RpcLogger logger )
63
- {
64
- var manager = new PowerShellManager ( logger ) ;
65
-
45
+ internal void InitializeRunspace ( )
46
+ {
66
47
// Add HttpResponseContext namespace so users can reference
67
48
// HttpResponseContext without needing to specify the full namespace
68
- manager . ExecuteScriptAndClearCommands ( $ "using namespace { typeof ( HttpResponseContext ) . Namespace } ") ;
69
- manager . ExecuteScriptAndClearCommands ( s_SetExecutionPolicyOnWindowsScript ) ;
70
- return manager ;
71
- }
72
-
73
- static string BuildBindingHashtableScript ( IDictionary < string , BindingInfo > outBindings )
74
- {
75
- // Since all of the out bindings are stored in variables at this point,
76
- // we must construct a script that will return those output bindings in a hashtable
77
- StringBuilder script = new StringBuilder ( ) ;
78
- script . AppendLine ( "@{" ) ;
79
- foreach ( KeyValuePair < string , BindingInfo > binding in outBindings )
80
- {
81
- script . Append ( "'" ) ;
82
- script . Append ( binding . Key ) ;
83
-
84
- // since $return has a dollar sign, we have to treat it differently
85
- if ( binding . Key == "$return" )
86
- {
87
- script . Append ( "' = " ) ;
88
- }
89
- else
90
- {
91
- script . Append ( "' = $" ) ;
92
- }
93
- script . AppendLine ( binding . Key ) ;
94
- }
95
- script . AppendLine ( "}" ) ;
96
-
97
- return script . ToString ( ) ;
98
- }
99
-
100
- void ResetRunspace ( )
101
- {
102
- // Reset the runspace to the Initial Session State
103
- _pwsh . Runspace . ResetRunspaceState ( ) ;
49
+ _pwsh . AddScript ( $ "using namespace { typeof ( HttpResponseContext ) . Namespace } ") . InvokeAndClearCommands ( ) ;
50
+
51
+ // Set the PSModulePath
52
+ Environment . SetEnvironmentVariable ( "PSModulePath" , Path . Join ( AppDomain . CurrentDomain . BaseDirectory , "Modules" ) ) ;
104
53
}
105
54
106
- void ExecuteScriptAndClearCommands ( string script )
107
- {
108
- _pwsh . AddScript ( script ) . Invoke ( ) ;
109
- _pwsh . Commands . Clear ( ) ;
110
- }
111
-
112
- public Collection < T > ExecuteScriptAndClearCommands < T > ( string script )
113
- {
114
- var result = _pwsh . AddScript ( script ) . Invoke < T > ( ) ;
115
- _pwsh . Commands . Clear ( ) ;
116
- return result ;
117
- }
118
-
119
- public PowerShellManager InvokeFunctionAndSetGlobalReturn (
55
+ internal Hashtable InvokeFunction (
120
56
string scriptPath ,
121
57
string entryPoint ,
122
58
Hashtable triggerMetadata ,
@@ -130,20 +66,25 @@ public PowerShellManager InvokeFunctionAndSetGlobalReturn(
130
66
// If it does, we invoke the command of that name. We also need to fetch
131
67
// the ParameterMetadata so that we can tell whether or not the user is asking
132
68
// for the $TriggerMetadata
133
-
134
69
using ( ExecutionTimer . Start ( _logger , "Parameter metadata retrieved." ) )
135
70
{
136
71
if ( entryPoint != "" )
137
72
{
138
- ExecuteScriptAndClearCommands ( $@ ". { scriptPath } ") ;
139
- parameterMetadata = ExecuteScriptAndClearCommands < FunctionInfo > ( $@ "Get-Command { entryPoint } ") [ 0 ] . Parameters ;
140
- _pwsh . AddScript ( $@ ". { entryPoint } @args") ;
73
+ parameterMetadata = _pwsh
74
+ . AddCommand ( "Microsoft.PowerShell.Core\\ Import-Module" ) . AddParameter ( "Name" , scriptPath )
75
+ . AddStatement ( )
76
+ . AddCommand ( "Microsoft.PowerShell.Core\\ Get-Command" ) . AddParameter ( "Name" , entryPoint )
77
+ . InvokeAndClearCommands < FunctionInfo > ( ) [ 0 ] . Parameters ;
78
+
79
+ _pwsh . AddCommand ( entryPoint ) ;
141
80
142
81
}
143
82
else
144
83
{
145
- parameterMetadata = ExecuteScriptAndClearCommands < ExternalScriptInfo > ( $@ "Get-Command { scriptPath } ") [ 0 ] . Parameters ;
146
- _pwsh . AddScript ( $@ ". { scriptPath } @args") ;
84
+ parameterMetadata = _pwsh . AddCommand ( "Microsoft.PowerShell.Core\\ Get-Command" ) . AddParameter ( "Name" , scriptPath )
85
+ . InvokeAndClearCommands < ExternalScriptInfo > ( ) [ 0 ] . Parameters ;
86
+
87
+ _pwsh . AddCommand ( scriptPath ) ;
147
88
}
148
89
}
149
90
@@ -154,41 +95,52 @@ public PowerShellManager InvokeFunctionAndSetGlobalReturn(
154
95
}
155
96
156
97
// Gives access to additional Trigger Metadata if the user specifies TriggerMetadata
157
- if ( parameterMetadata . ContainsKey ( s_TriggerMetadataParameterName ) )
98
+ if ( parameterMetadata . ContainsKey ( _TriggerMetadataParameterName ) )
158
99
{
159
- _pwsh . AddParameter ( s_TriggerMetadataParameterName , triggerMetadata ) ;
100
+ _pwsh . AddParameter ( _TriggerMetadataParameterName , triggerMetadata ) ;
160
101
_logger . LogDebug ( $ "TriggerMetadata found. Value:{ Environment . NewLine } { triggerMetadata . ToString ( ) } ") ;
161
102
}
162
103
163
- // This script handles when the user adds something to the pipeline.
104
+ PSObject returnObject = null ;
164
105
using ( ExecutionTimer . Start ( _logger , "Execution of the user's function completed." ) )
165
106
{
166
- ExecuteScriptAndClearCommands ( s_LogAndSetReturnValueScript ) ;
107
+ // Log everything we received from the pipeline and set the last one to be the ReturnObject
108
+ Collection < PSObject > pipelineItems = _pwsh . InvokeAndClearCommands < PSObject > ( ) ;
109
+ foreach ( var psobject in pipelineItems )
110
+ {
111
+ _logger . LogInformation ( $ "OUTPUT: { psobject . ToString ( ) } ") ;
112
+ }
113
+
114
+ returnObject = pipelineItems [ pipelineItems . Count - 1 ] ;
115
+ }
116
+
117
+ var result = _pwsh . AddCommand ( "Azure.Functions.PowerShell.Worker.Module\\ Get-OutputBinding" )
118
+ . AddParameter ( "Purge" )
119
+ . InvokeAndClearCommands < Hashtable > ( ) [ 0 ] ;
120
+
121
+ if ( returnObject != null )
122
+ {
123
+ result . Add ( "$return" , returnObject ) ;
167
124
}
168
- return this ;
125
+ return result ;
169
126
}
170
- catch ( Exception e )
127
+ finally
171
128
{
172
- ResetRunspace ( ) ;
173
- throw e ;
129
+ ResetRunspace ( scriptPath ) ;
174
130
}
175
131
}
176
132
177
- public Hashtable ReturnBindingHashtable ( IDictionary < string , BindingInfo > outBindings )
133
+ private void ResetRunspace ( string scriptPath )
178
134
{
179
- try
180
- {
181
- // This script returns a hashtable that contains the
182
- // output bindings that we will return to the function host.
183
- var result = ExecuteScriptAndClearCommands < Hashtable > ( BuildBindingHashtableScript ( outBindings ) ) [ 0 ] ;
184
- ResetRunspace ( ) ;
185
- return result ;
186
- }
187
- catch ( Exception e )
188
- {
189
- ResetRunspace ( ) ;
190
- throw e ;
191
- }
135
+ // Reset the runspace to the Initial Session State
136
+ _pwsh . Runspace . ResetRunspaceState ( ) ;
137
+
138
+ // If the function had an entry point, this will remove the module that was loaded
139
+ var moduleName = Path . GetFileNameWithoutExtension ( scriptPath ) ;
140
+ _pwsh . AddCommand ( "Microsoft.PowerShell.Core\\ Remove-Module" )
141
+ . AddParameter ( "Name" , moduleName )
142
+ . AddParameter ( "ErrorAction" , "SilentlyContinue" )
143
+ . InvokeAndClearCommands ( ) ;
192
144
}
193
145
}
194
146
}
0 commit comments