From 85acbe97c4d96bf7244951de155ff3f9696653f9 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Thu, 15 Feb 2024 16:49:43 -0700 Subject: [PATCH 1/2] Set PSModulePath for first powershell early --- src/FunctionLoader.cs | 8 +++++- src/RequestProcessor.cs | 2 +- src/WorkerIndexing/WorkerIndexingHelper.cs | 30 ++++++++++++++-------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/FunctionLoader.cs b/src/FunctionLoader.cs index 1611ccdd..518671dd 100644 --- a/src/FunctionLoader.cs +++ b/src/FunctionLoader.cs @@ -74,11 +74,17 @@ internal static void ClearLoadedFunctions() /// internal static void SetupWellKnownPaths(string functionAppRootPath, string managedDependenciesPath) { + var workerLevelModulesPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Modules"); + + if (functionAppRootPath == null) + { + FunctionModulePath = workerLevelModulesPath; + } + FunctionAppRootPath = functionAppRootPath; // Resolve module paths var appLevelModulesPath = Path.Join(FunctionAppRootPath, "Modules"); - var workerLevelModulesPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Modules"); FunctionModulePath = $"{appLevelModulesPath}{Path.PathSeparator}{workerLevelModulesPath}"; // Add the managed dependencies folder path diff --git a/src/RequestProcessor.cs b/src/RequestProcessor.cs index ed29a40a..0fadcaf5 100644 --- a/src/RequestProcessor.cs +++ b/src/RequestProcessor.cs @@ -401,7 +401,7 @@ private StreamingMessage ProcessFunctionMetadataRequest(StreamingMessage request rpcLogger.SetContext(request.RequestId, null); _functionAppRootPath = request.FunctionsMetadataRequest.FunctionAppDirectory; - response.FunctionMetadataResponse.FunctionMetadataResults.AddRange(WorkerIndexingHelper.IndexFunctions(_functionAppRootPath, rpcLogger)); + response.FunctionMetadataResponse.FunctionMetadataResults.AddRange(WorkerIndexingHelper.IndexFunctions(_functionAppRootPath, rpcLogger, _firstPwshInstance)); return response; } diff --git a/src/WorkerIndexing/WorkerIndexingHelper.cs b/src/WorkerIndexing/WorkerIndexingHelper.cs index 87f2bc21..925361ad 100644 --- a/src/WorkerIndexing/WorkerIndexingHelper.cs +++ b/src/WorkerIndexing/WorkerIndexingHelper.cs @@ -26,7 +26,7 @@ internal class WorkerIndexingHelper const string AzureFunctionsPowerShellSDKModuleName = "AzureFunctions.PowerShell.SDK"; private static readonly ErrorRecordFormatter _errorRecordFormatter = new ErrorRecordFormatter(); - internal static IEnumerable IndexFunctions(string functionAppRootPath, ILogger logger) + internal static IEnumerable IndexFunctions(string functionAppRootPath, ILogger logger, System.Management.Automation.PowerShell _firstPwshInstance) { if (string.IsNullOrWhiteSpace(functionAppRootPath)) { @@ -35,6 +35,14 @@ internal static IEnumerable IndexFunctions(string functionA List indexedFunctions = new List(); + // A hacky way to add the worker modules into the env only + FunctionLoader.SetupWellKnownPaths(null, null); + + _firstPwshInstance.AddCommand("Microsoft.PowerShell.Management\\Set-Content") + .AddParameter("Path", "env:PSModulePath") + .AddParameter("Value", FunctionLoader.FunctionModulePath) + .InvokeAndClearCommands(); + // This is not the correct way to deal with getting a runspace for the cmdlet. // Firstly, creating a runspace is expensive. If we are going to generate a runspace, it should be done on @@ -55,11 +63,11 @@ internal static IEnumerable IndexFunctions(string functionA // 3. Continue using a new runspace for invoking Get-FunctionsMetadata, but initialize it in worker init and // point the PsModulePath to the module path bundled with the worker. - InitialSessionState initial = InitialSessionState.CreateDefault(); - Runspace runspace = RunspaceFactory.CreateRunspace(initial); - runspace.Open(); - System.Management.Automation.PowerShell _powershell = System.Management.Automation.PowerShell.Create(); - _powershell.Runspace = runspace; + //InitialSessionState initial = InitialSessionState.CreateDefault(); + //Runspace runspace = RunspaceFactory.CreateRunspace(initial); + //runspace.Open(); + //System.Management.Automation.PowerShell _powershell = System.Management.Automation.PowerShell.Create(); + //_powershell.Runspace = runspace; string outputString = string.Empty; Exception exception = null; @@ -70,9 +78,9 @@ internal static IEnumerable IndexFunctions(string functionA try { - _powershell.AddCommand(GetFunctionsMetadataCmdletName).AddArgument(functionAppRootPath); - results = _powershell.Invoke(); - cmdletExecutionHadErrors = _powershell.HadErrors; + _firstPwshInstance.AddCommand(GetFunctionsMetadataCmdletName).AddArgument(functionAppRootPath); + results = _firstPwshInstance.Invoke(); + cmdletExecutionHadErrors = _firstPwshInstance.HadErrors; } catch (Exception ex) { @@ -88,7 +96,7 @@ internal static IEnumerable IndexFunctions(string functionA } else if (cmdletExecutionHadErrors) { - var errorCollection = _powershell.Streams.Error; + var errorCollection = _firstPwshInstance.Streams.Error; var stringBuilder = new StringBuilder(); foreach (var errorRecord in errorCollection) @@ -106,7 +114,7 @@ internal static IEnumerable IndexFunctions(string functionA throw new Exception(errorMsg); } - _powershell.Commands.Clear(); + _firstPwshInstance.Commands.Clear(); } // TODO: The GetFunctionsMetadataCmdlet should never return more than one result. Make sure that this is the case and remove this code. From 8e7634a2b73866d4691b562c65f7c1c6e8e97f71 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Thu, 29 Feb 2024 10:17:24 -0700 Subject: [PATCH 2/2] Pass a bool for more robust logic --- src/FunctionLoader.cs | 5 +- .../AzureFunctions.AttributeDefinitions.ps1 | 65 ++++++++++ .../0.0.3/AzureFunctions.PowerShell.SDK.psd1 | Bin 0 -> 9332 bytes .../0.0.3/PSGetModuleInfo.xml | 121 ++++++++++++++++++ src/PowerShell/PowerShellManager.cs | 2 +- src/RequestProcessor.cs | 2 +- src/WorkerIndexing/WorkerIndexingHelper.cs | 2 +- test/Unit/Modules/HelperModuleTests.cs | 2 +- .../Unit/PowerShell/PowerShellManagerTests.cs | 2 +- 9 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.AttributeDefinitions.ps1 create mode 100644 src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.PowerShell.SDK.psd1 create mode 100644 src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/PSGetModuleInfo.xml diff --git a/src/FunctionLoader.cs b/src/FunctionLoader.cs index 518671dd..3934e340 100644 --- a/src/FunctionLoader.cs +++ b/src/FunctionLoader.cs @@ -72,13 +72,14 @@ internal static void ClearLoadedFunctions() /// Setup the well known paths about the FunctionApp. /// This method is called only once during the code start. /// - internal static void SetupWellKnownPaths(string functionAppRootPath, string managedDependenciesPath) + internal static void SetupWellKnownPaths(string functionAppRootPath, string managedDependenciesPath, bool functionAppRootIsUnknown) { var workerLevelModulesPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Modules"); - if (functionAppRootPath == null) + if (functionAppRootIsUnknown) { FunctionModulePath = workerLevelModulesPath; + return; } FunctionAppRootPath = functionAppRootPath; diff --git a/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.AttributeDefinitions.ps1 b/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.AttributeDefinitions.ps1 new file mode 100644 index 00000000..0385b9e2 --- /dev/null +++ b/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.AttributeDefinitions.ps1 @@ -0,0 +1,65 @@ +# +# Copyright (c) Microsoft. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +class Function : Attribute { + [string]$Name +} + +class HttpTrigger : Attribute { + [string]$AuthLevel + [string[]]$Methods + [string]$Route +} + +class HttpOutput : Attribute { + [string]$Name +} + +class TimerTrigger : Attribute { + [string]$Chron +} + +class EventGridTrigger : Attribute { + EventGridTrigger() { } +} + +class DurableClient : Attribute { + [string]$Name +} + +class OrchestrationTrigger : Attribute { +} + +class ActivityTrigger : Attribute { +} + +class EventHubTrigger : Attribute { + [string]$EventHubName + [string]$ConsumerGroup + [string]$Cardinality + [string]$Connection +} + +class EventHubOutput : Attribute { + [string]$Name + [string]$EventHubName + [string]$Connection +} + +class InputBinding : Attribute { + [string]$Type + [string]$Name +} + +class OutputBinding : Attribute { + [string]$Type + [string]$Name +} + +class AdditionalInformation : Attribute { + [string]$BindingName + [string]$Name + $Value +} \ No newline at end of file diff --git a/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.PowerShell.SDK.psd1 b/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/AzureFunctions.PowerShell.SDK.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..a52718164c31f27811ac77b36d5e27d34d6e09a7 GIT binary patch literal 9332 zcmeI2U2hXd6o%)zQvbtBxk#u=fItgWs)Usghzb-zpuKe*C%7bz>lgy9`0H)oXO0gu zW3P7;L)xmUB71keJ7>=4dpOwZd=D;=;`T!o*io4dU&d{Gu<19=i!x(NBUdW`BwNLtWNiK z4j$YUC~RW&l}0K z7k{nv+rbPcN!pT&eCJu3#>aJ8p3lt|j-HEy(bTT)2T!H-`_ePIaqr3{!55CT#{6I} zYPfDq72xGwSQ8KCKYg8DDfF4s#ojye)=E5`PJMEsy|^Yme$^4L#?AxrhX=#qLHq?f zx&@Qoa{3R#iz$86(GAhKF3ugVdx1>iq57%|SBTqciG4TX? zfX(>sIH1pcMPRdJTk(0&)-cYIA^8c~JctpH(7vmkiB2M6q_rp7%_72ayhOD6cv!Tz z>0!4vo+@bv>_Ye8E;8fa;H=>V@+V82Maj57l;tX|8cHj04Pt|Y_It`~RZVWIl-JPE zM1RwMf|Wg-^@ zJJ#xBt;9D>ddmZ)wSS6F6NzV$_j-QN!(n_9Uhq5caS+cg;stfo>vPI&dGkh#qzM6* zkqMNv3TSzGRu)+B;gojb(EPkPJ}<*^e}|Oy7W^Et)@7mf@I)2Et~9?b|Faz6(Q#4V z1X~mKfNsQ!dpC#y26$Y*)ovgU9`99ia~vD$y^e@5(kf8ekjrCnD2ngp>v=xLtG47p z6G__F_q?2!r*W4D4xb{wT6a~cW_=R%+z(&txt~?%eH+g@(*M5BZ!MwKS$~@6sI!ot z^I=uW^Oo~oNkVcUdBNz`~RAnFReJzr$38_Ys@kaDWG;KD=x2-Y&6})0{>-M5u>5ZvfpmP}(E-^Hh;ZzuZ zqDm`|$zN>HPPCEY)tjp7dH2W|5jzCuGKZu)^+X*??<9Q4zYz|4pQxlO6uIo6<{++g;k9wj+TL#`mu*ijEn;Jh(PO)p-xR|QzM_SSxspb0My_P zsb;6Dbs8#BZC>CKr~!5j0bWjXur#=iBDP1-5_ ztJG$_P1>u~EzR0D73Ufq2-clmD`m$|{w7~yUHoQt>dPJGq`X@+WUOM}ajI`{($Fur zIT)JHS)g2c4OifBLR-)3XNas_ma%t?b#H3?w^F9_uJY9@l4(~G97gMP z6$K;7>DA;UYO=nja{Yq>_=LT`Wj>YSrnEoWB(|CTQ_Wa?L%m9e&aTwLm3Xljc|< z)8c8TQ}AOm&qRZD0=!+{6X!mIoadhT!AbZ<&oNDE*R*xO{`}|VpLZ?v{r1Mjat!>L zkJ+TKX+G1={mzv3r|`|xhnm+iFH<*A5%f#lT64B=EBbLUMZ9@6{s%^SKi=eFJJO)d zh7GSu%FX&|_r4F}2G6^%UBEb#w?rVOIVGy*{O0G2sO7mP;oIc2l0g+~1_i MS1#7whxqp5FS0|Uh5!Hn literal 0 HcmV?d00001 diff --git a/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/PSGetModuleInfo.xml b/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/PSGetModuleInfo.xml new file mode 100644 index 00000000..8e5b640e --- /dev/null +++ b/src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/PSGetModuleInfo.xml @@ -0,0 +1,121 @@ + + + + Microsoft.PowerShell.Commands.PSRepositoryItemInfo + System.Management.Automation.PSCustomObject + System.Object + + + AzureFunctions.PowerShell.SDK + 0.0.3 + Module + This module contains utilities for writing and parsing Azure Functions written in PowerShell and it is to be used within the PowerShell language worker + Microsoft Corporation + Francisco-Gamino + (c) 2022 Microsoft. All rights reserved. +
2023-06-05T03:55:02-06:00
+ + + + + + + + System.Object[] + System.Array + System.Object + + + PSModule + PSEdition_Core + + + + + System.Collections.Hashtable + System.Object + + + + Cmdlet + + + + Get-FunctionsMetadata + + + + + Workflow + + + + + + + Function + + + + Command + + + + Get-FunctionsMetadata + + + + + DscResource + + + + RoleCapability + + + + + + # 0.0.3_x000D__x000A_ Initial Release. + + + + + https://www.powershellgallery.com/api/v2 + PSGallery + NuGet + + + System.Management.Automation.PSCustomObject + System.Object + + + (c) 2022 Microsoft. All rights reserved. + This module contains utilities for writing and parsing Azure Functions written in PowerShell and it is to be used within the PowerShell language worker + False + # 0.0.3_x000D__x000A_ Initial Release. + True + True + 215 + 261 + 19534 + 6/5/2023 3:55:02 AM -06:00 + 6/5/2023 3:55:02 AM -06:00 + 2/15/2024 7:12:43 AM -07:00 + PSModule PSEdition_Core PSCmdlet_Get-FunctionsMetadata PSCommand_Get-FunctionsMetadata PSIncludes_Cmdlet + False + 2024-02-15T07:12:43Z + 0.0.3 + Microsoft Corporation + false + Module + AzureFunctions.PowerShell.SDK.nuspec|AzureFunctions.AttributeDefinitions.ps1|AzureFunctions.PowerShell.SDK.dll|AzureFunctions.PowerShell.SDK.psd1 + 1d075857-4919-4ede-bb43-bb193483a280 + 7.2 + Microsoft Corporation + + + D:\repos\azure-functions-powershell-worker\src\Modules\AzureFunctions.PowerShell.SDK\0.0.3 +
+
+
diff --git a/src/PowerShell/PowerShellManager.cs b/src/PowerShell/PowerShellManager.cs index 5a0a4db7..499032b9 100644 --- a/src/PowerShell/PowerShellManager.cs +++ b/src/PowerShell/PowerShellManager.cs @@ -224,7 +224,7 @@ public Hashtable InvokeFunction( try { - if(functionInfo.DurableFunctionInfo.IsOrchestrationFunction) + if (functionInfo.DurableFunctionInfo.IsOrchestrationFunction) { return durableFunctionsUtils.InvokeOrchestrationFunction(); } diff --git a/src/RequestProcessor.cs b/src/RequestProcessor.cs index 0fadcaf5..03cb0bb4 100644 --- a/src/RequestProcessor.cs +++ b/src/RequestProcessor.cs @@ -574,7 +574,7 @@ private static void BindOutputFromResult(InvocationResponse response, AzFunction private void SetupAppRootPathAndModulePath(string functionAppRootPath, string managedDependenciesPath) { - FunctionLoader.SetupWellKnownPaths(functionAppRootPath, managedDependenciesPath); + FunctionLoader.SetupWellKnownPaths(functionAppRootPath, managedDependenciesPath, false); if (FunctionLoader.FunctionAppRootPath == null) { diff --git a/src/WorkerIndexing/WorkerIndexingHelper.cs b/src/WorkerIndexing/WorkerIndexingHelper.cs index 925361ad..11abb8ce 100644 --- a/src/WorkerIndexing/WorkerIndexingHelper.cs +++ b/src/WorkerIndexing/WorkerIndexingHelper.cs @@ -36,7 +36,7 @@ internal static IEnumerable IndexFunctions(string functionA List indexedFunctions = new List(); // A hacky way to add the worker modules into the env only - FunctionLoader.SetupWellKnownPaths(null, null); + FunctionLoader.SetupWellKnownPaths(null, null, true); _firstPwshInstance.AddCommand("Microsoft.PowerShell.Management\\Set-Content") .AddParameter("Path", "env:PSModulePath") diff --git a/test/Unit/Modules/HelperModuleTests.cs b/test/Unit/Modules/HelperModuleTests.cs index 315ca200..5dcab06e 100644 --- a/test/Unit/Modules/HelperModuleTests.cs +++ b/test/Unit/Modules/HelperModuleTests.cs @@ -49,7 +49,7 @@ static HelperModuleTests() }; var funcLoadReq = new FunctionLoadRequest { FunctionId = "FunctionId", Metadata = rpcFuncMetadata }; - FunctionLoader.SetupWellKnownPaths(funcLoadReq.Metadata.Directory, managedDependenciesPath: null); + FunctionLoader.SetupWellKnownPaths(funcLoadReq.Metadata.Directory, managedDependenciesPath: null, false); s_pwsh = Utils.NewPwshInstance(); s_funcInfo = new AzFunctionInfo(rpcFuncMetadata); } diff --git a/test/Unit/PowerShell/PowerShellManagerTests.cs b/test/Unit/PowerShell/PowerShellManagerTests.cs index 726f5e4a..2a570321 100644 --- a/test/Unit/PowerShell/PowerShellManagerTests.cs +++ b/test/Unit/PowerShell/PowerShellManagerTests.cs @@ -80,7 +80,7 @@ static PowerShellManagerTests() }; s_functionLoadRequest = new FunctionLoadRequest { FunctionId = "FunctionId", Metadata = rpcFunctionMetadata }; - FunctionLoader.SetupWellKnownPaths(s_functionLoadRequest.Metadata.Directory, managedDependenciesPath: null); + FunctionLoader.SetupWellKnownPaths(s_functionLoadRequest.Metadata.Directory, managedDependenciesPath: null, false); } // Have a single place to get a PowerShellManager for testing.