From 7a65dc294eb4e92f7d81666acdd22c0a486e618d Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Thu, 7 Aug 2025 22:08:23 +0000 Subject: [PATCH 01/15] Adding alternative shells support for running hooks. This allows pwsh to try running in pwsh5 when pwsh7 is not found in windows. User can set templates to be ShellStrict and fail if pwsh7 is not found --- cli/azd/pkg/ext/hooks_runner.go | 1 + cli/azd/pkg/ext/models.go | 3 +++ cli/azd/pkg/tools/powershell/powershell.go | 18 +++++++++++++++++- cli/azd/pkg/tools/script.go | 3 +++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/ext/hooks_runner.go b/cli/azd/pkg/ext/hooks_runner.go index 66448a581c6..de6cc344450 100644 --- a/cli/azd/pkg/ext/hooks_runner.go +++ b/cli/azd/pkg/ext/hooks_runner.go @@ -191,6 +191,7 @@ func (h *HooksRunner) execHook(ctx context.Context, hookConfig *HookConfig, opti defer h.console.StopPreviewer(ctx, false) } options.UserPwsh = string(hookConfig.Shell) + options.StrictShell = hookConfig.StrictShell log.Printf("Executing script '%s'\n", hookConfig.path) res, err := script.Execute(ctx, hookConfig.path, *options) diff --git a/cli/azd/pkg/ext/models.go b/cli/azd/pkg/ext/models.go index 531c4631314..d6d76c2858b 100644 --- a/cli/azd/pkg/ext/models.go +++ b/cli/azd/pkg/ext/models.go @@ -63,6 +63,9 @@ type HookConfig struct { Name string `yaml:",omitempty"` // The type of script hook (bash or powershell) Shell ShellType `yaml:"shell,omitempty"` + // If true, azd won't try to use alternative shell to execute the command. For example, pwsh won't be tried with + // powershell (pwsh5) in Windows. + StrictShell bool `yaml:"strictShell,omitempty"` // The inline script to execute or path to existing file Run string `yaml:"run,omitempty"` // When set to true will not halt command execution even when a script error occurs. diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 9a5c43c6043..82194fa633f 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -6,6 +6,7 @@ package powershell import ( "context" "fmt" + "runtime" "strings" "github.com/azure/azure-dev/cli/azd/internal" @@ -54,7 +55,13 @@ func checkPath(options tools.ExecOptions) (err error) { // Executes the specified powershell script // When interactive is true will attach to stdin, stdout & stderr func (bs *powershellScript) Execute(ctx context.Context, path string, options tools.ExecOptions) (exec.RunResult, error) { - if err := bs.checkInstalled(options); err != nil { + // block alternative shells for non-windows. + // This is because the only alternative shell supported is powershell5 which is not available on non-windows platforms. + if runtime.GOOS != "windows" { + options.StrictShell = true + } + + if err := bs.checkInstalled(options); err != nil && options.StrictShell { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, Suggestion: fmt.Sprintf("PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", @@ -62,6 +69,15 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to } } + // non-strict mode, check for alternative shell powershell 5 + options.UserPwsh = "powershell" + if err := bs.checkInstalled(options); err != nil { + return exec.RunResult{}, &internal.ErrorWithSuggestion{ + Err: err, + Suggestion: "Make sure Powershell is installed in your system.", + } + } + runArgs := exec.NewRunArgs(options.UserPwsh, path). WithCwd(bs.cwd). WithEnv(bs.envVars). diff --git a/cli/azd/pkg/tools/script.go b/cli/azd/pkg/tools/script.go index 2526b78bdf3..88e45673395 100644 --- a/cli/azd/pkg/tools/script.go +++ b/cli/azd/pkg/tools/script.go @@ -15,6 +15,9 @@ type ExecOptions struct { Interactive *bool StdOut io.Writer UserPwsh string + // If true, azd won't try to use alternative shell to execute the command. For example, pwsh won't be tried with + // powershell (pwsh5) in Windows. + StrictShell bool } // Utility to easily execute a bash script across platforms From 46a913767004d103d2938e1d8255f42c36915619 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Thu, 7 Aug 2025 22:33:10 +0000 Subject: [PATCH 02/15] schema update --- schemas/v1.0/azure.yaml.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/schemas/v1.0/azure.yaml.json b/schemas/v1.0/azure.yaml.json index 52eafc80307..0b6e2cfd7a6 100644 --- a/schemas/v1.0/azure.yaml.json +++ b/schemas/v1.0/azure.yaml.json @@ -704,6 +704,12 @@ "title": "Required. The inline script or relative path of your scripts from the project or service path", "description": "When specifying an inline script you also must specify the `shell` to use. This is automatically inferred when using paths." }, + "strictShell": { + "type": "boolean", + "default": false, + "title": "Whether to use strict shell mode", + "description": "Optional. When set to true will only allow the specified shell to be used. (Default: false). Some shells like pwsh will try powershell 7 first and powershell 5 (in Windows) as an alternative in non strict mode." + }, "continueOnError": { "type": "boolean", "default": false, From b2863bfaa92837a75d0e55059d729f599bc29353 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Thu, 7 Aug 2025 22:51:27 +0000 Subject: [PATCH 03/15] fix alternative impl --- cli/azd/pkg/tools/powershell/powershell.go | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 82194fa633f..52ad22f4f50 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -61,20 +61,22 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to options.StrictShell = true } - if err := bs.checkInstalled(options); err != nil && options.StrictShell { - return exec.RunResult{}, &internal.ErrorWithSuggestion{ - Err: err, - Suggestion: fmt.Sprintf("PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", - output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), + if err := bs.checkInstalled(options); err != nil { + if options.StrictShell { + return exec.RunResult{}, &internal.ErrorWithSuggestion{ + Err: err, + Suggestion: fmt.Sprintf("PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", + output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), + } } - } - // non-strict mode, check for alternative shell powershell 5 - options.UserPwsh = "powershell" - if err := bs.checkInstalled(options); err != nil { - return exec.RunResult{}, &internal.ErrorWithSuggestion{ - Err: err, - Suggestion: "Make sure Powershell is installed in your system.", + // non-strict mode, check for alternative shell powershell 5 + options.UserPwsh = "powershell" + if err := bs.checkInstalled(options); err != nil { + return exec.RunResult{}, &internal.ErrorWithSuggestion{ + Err: err, + Suggestion: "Make sure Powershell is installed in your system.", + } } } From 5abc9c7eb03d8feef90b85f63f6e6862ec093a4d Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Thu, 7 Aug 2025 22:53:58 +0000 Subject: [PATCH 04/15] lint --- cli/azd/pkg/tools/powershell/powershell.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 52ad22f4f50..32a1ec80c0f 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -65,7 +65,8 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to if options.StrictShell { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, - Suggestion: fmt.Sprintf("PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", + Suggestion: fmt.Sprintf( + "PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), } } From ac63fdcb47533096d7567ad04879ca932014643b Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Thu, 7 Aug 2025 17:20:40 -0700 Subject: [PATCH 05/15] tests for win --- .../pkg/tools/powershell/powershell_test.go | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell_test.go b/cli/azd/pkg/tools/powershell/powershell_test.go index 317fac4f4ae..6afd91896a4 100644 --- a/cli/azd/pkg/tools/powershell/powershell_test.go +++ b/cli/azd/pkg/tools/powershell/powershell_test.go @@ -6,6 +6,7 @@ package powershell import ( "context" "errors" + "runtime" "strings" "testing" @@ -57,6 +58,53 @@ func Test_Powershell_Execute(t *testing.T) { require.NoError(t, err) }) + t.Run("Success - alternative", func(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("pwsh alternative is only for Windows") + } + mockContext := mocks.NewMockContext(context.Background()) + + // #nosec G101 + userPwsh := "pwsh -NoProfile" + mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool { + return strings.Contains(args.Cmd, userPwsh) + }).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) { + require.Equal(t, userPwsh, args.Cmd) + require.Equal(t, workingDir, args.Cwd) + require.Equal(t, scriptPath, args.Args[0]) + require.Equal(t, env, args.Env) + + return exec.NewRunResult(1, "not found", "not found"), nil + }) + + mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool { + return strings.Contains(args.Cmd, "powershell") + }).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) { + require.Equal(t, userPwsh, args.Cmd) + require.Equal(t, workingDir, args.Cwd) + require.Equal(t, scriptPath, args.Args[0]) + require.Equal(t, env, args.Env) + + return exec.NewRunResult(0, "", ""), nil + }) + + PowershellScript := NewPowershellScriptWithMockCheckPath( + mockContext.CommandRunner, + workingDir, + env, + func(options tools.ExecOptions) error { + return nil + }) + runResult, err := PowershellScript.Execute( + *mockContext.Context, + scriptPath, + tools.ExecOptions{UserPwsh: userPwsh, Interactive: to.Ptr(true)}, + ) + + require.NotNil(t, runResult) + require.NoError(t, err) + }) + t.Run("Error", func(t *testing.T) { mockContext := mocks.NewMockContext(context.Background()) @@ -90,7 +138,7 @@ func Test_Powershell_Execute(t *testing.T) { _, err := PowershellScript.Execute( *mockContext.Context, scriptPath, - tools.ExecOptions{Interactive: to.Ptr(true)}, + tools.ExecOptions{Interactive: to.Ptr(true), StrictShell: true}, ) require.Error(t, err) From 5efee9d8f1149874fff17d33bc68358bd57b8557 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Fri, 8 Aug 2025 12:01:53 -0700 Subject: [PATCH 06/15] Apply suggestion from @JeffreyCA Co-authored-by: JeffreyCA --- cli/azd/pkg/tools/powershell/powershell.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 32a1ec80c0f..412873d6cbc 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -76,7 +76,7 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to if err := bs.checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, - Suggestion: "Make sure Powershell is installed in your system.", + Suggestion: "Make sure PowerShell is installed on your system.", } } } From 0f2a7490ef20c63f001224595e0e6a6d5d17ae30 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Fri, 8 Aug 2025 19:03:39 +0000 Subject: [PATCH 07/15] alpha schema --- schemas/alpha/azure.yaml.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/schemas/alpha/azure.yaml.json b/schemas/alpha/azure.yaml.json index 49981e1e90c..f58646aafd6 100644 --- a/schemas/alpha/azure.yaml.json +++ b/schemas/alpha/azure.yaml.json @@ -744,6 +744,12 @@ "title": "Required. The inline script or relative path of your scripts from the project or service path", "description": "When specifying an inline script you also must specify the `shell` to use. This is automatically inferred when using paths." }, + "strictShell": { + "type": "boolean", + "default": false, + "title": "Whether to use strict shell mode", + "description": "Optional. When set to true will only allow the specified shell to be used. (Default: false). Some shells like pwsh will try powershell 7 first and powershell 5 (in Windows) as an alternative in non strict mode." + }, "continueOnError": { "type": "boolean", "default": false, From 20c5c8a3c45810d9930d04221de1c75633a4c5c5 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Fri, 8 Aug 2025 12:23:57 -0700 Subject: [PATCH 08/15] fix test --- cli/azd/pkg/tools/powershell/powershell_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell_test.go b/cli/azd/pkg/tools/powershell/powershell_test.go index 6afd91896a4..8199ac54edc 100644 --- a/cli/azd/pkg/tools/powershell/powershell_test.go +++ b/cli/azd/pkg/tools/powershell/powershell_test.go @@ -6,6 +6,7 @@ package powershell import ( "context" "errors" + "fmt" "runtime" "strings" "testing" @@ -77,10 +78,11 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(1, "not found", "not found"), nil }) + userPwshAlternative := "powershell" mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool { - return strings.Contains(args.Cmd, "powershell") + return strings.Contains(args.Cmd, userPwshAlternative) }).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) { - require.Equal(t, userPwsh, args.Cmd) + require.Equal(t, userPwshAlternative, args.Cmd) require.Equal(t, workingDir, args.Cwd) require.Equal(t, scriptPath, args.Args[0]) require.Equal(t, env, args.Env) @@ -93,6 +95,9 @@ func Test_Powershell_Execute(t *testing.T) { workingDir, env, func(options tools.ExecOptions) error { + if strings.Contains(options.UserPwsh, "pwsh") { + return fmt.Errorf("failed to find PowerShell executable") + } return nil }) runResult, err := PowershellScript.Execute( From 6e952bf207033551e7d25ac7085ef894880976bb Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 11 Aug 2025 19:25:29 +0000 Subject: [PATCH 09/15] remove strictShell and add shell name after using alternative on error --- cli/azd/pkg/ext/hooks_runner.go | 1 - cli/azd/pkg/ext/models.go | 3 -- cli/azd/pkg/tools/powershell/powershell.go | 32 ++++++++++++------- .../pkg/tools/powershell/powershell_test.go | 2 +- cli/azd/pkg/tools/script.go | 3 -- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/cli/azd/pkg/ext/hooks_runner.go b/cli/azd/pkg/ext/hooks_runner.go index de6cc344450..66448a581c6 100644 --- a/cli/azd/pkg/ext/hooks_runner.go +++ b/cli/azd/pkg/ext/hooks_runner.go @@ -191,7 +191,6 @@ func (h *HooksRunner) execHook(ctx context.Context, hookConfig *HookConfig, opti defer h.console.StopPreviewer(ctx, false) } options.UserPwsh = string(hookConfig.Shell) - options.StrictShell = hookConfig.StrictShell log.Printf("Executing script '%s'\n", hookConfig.path) res, err := script.Execute(ctx, hookConfig.path, *options) diff --git a/cli/azd/pkg/ext/models.go b/cli/azd/pkg/ext/models.go index d6d76c2858b..531c4631314 100644 --- a/cli/azd/pkg/ext/models.go +++ b/cli/azd/pkg/ext/models.go @@ -63,9 +63,6 @@ type HookConfig struct { Name string `yaml:",omitempty"` // The type of script hook (bash or powershell) Shell ShellType `yaml:"shell,omitempty"` - // If true, azd won't try to use alternative shell to execute the command. For example, pwsh won't be tried with - // powershell (pwsh5) in Windows. - StrictShell bool `yaml:"strictShell,omitempty"` // The inline script to execute or path to existing file Run string `yaml:"run,omitempty"` // When set to true will not halt command execution even when a script error occurs. diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 412873d6cbc..7e12526e187 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -55,16 +55,12 @@ func checkPath(options tools.ExecOptions) (err error) { // Executes the specified powershell script // When interactive is true will attach to stdin, stdout & stderr func (bs *powershellScript) Execute(ctx context.Context, path string, options tools.ExecOptions) (exec.RunResult, error) { - // block alternative shells for non-windows. - // This is because the only alternative shell supported is powershell5 which is not available on non-windows platforms. - if runtime.GOOS != "windows" { - options.StrictShell = true - } + noPwshError := bs.checkInstalled(options) + if noPwshError != nil { - if err := bs.checkInstalled(options); err != nil { - if options.StrictShell { + if runtime.GOOS != "windows" { return exec.RunResult{}, &internal.ErrorWithSuggestion{ - Err: err, + Err: noPwshError, Suggestion: fmt.Sprintf( "PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), @@ -75,8 +71,10 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to options.UserPwsh = "powershell" if err := bs.checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ - Err: err, - Suggestion: "Make sure PowerShell is installed on your system.", + Err: err, + Suggestion: fmt.Sprintf( + "Make sure pwsh (Powershell 7) or powershell (Powershell 5) is installed on your system, visit %s", + output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), } } } @@ -94,5 +92,17 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to runArgs = runArgs.WithStdOut(options.StdOut) } - return bs.commandRunner.Run(ctx, runArgs) + result, err := bs.commandRunner.Run(ctx, runArgs) + if err != nil { + if noPwshError != nil { + err = &internal.ErrorWithSuggestion{ + Err: err, + Suggestion: "pwsh (Powershell 7) was not found and powershell (Powershell 5) was automatically used " + + "instead. You can try installing pwsh and trying again in case this script is not compatible with " + + "Powershell 5.", + } + } + } + + return result, err } diff --git a/cli/azd/pkg/tools/powershell/powershell_test.go b/cli/azd/pkg/tools/powershell/powershell_test.go index 6afd91896a4..16a1ebd2686 100644 --- a/cli/azd/pkg/tools/powershell/powershell_test.go +++ b/cli/azd/pkg/tools/powershell/powershell_test.go @@ -138,7 +138,7 @@ func Test_Powershell_Execute(t *testing.T) { _, err := PowershellScript.Execute( *mockContext.Context, scriptPath, - tools.ExecOptions{Interactive: to.Ptr(true), StrictShell: true}, + tools.ExecOptions{Interactive: to.Ptr(true)}, ) require.Error(t, err) diff --git a/cli/azd/pkg/tools/script.go b/cli/azd/pkg/tools/script.go index 88e45673395..2526b78bdf3 100644 --- a/cli/azd/pkg/tools/script.go +++ b/cli/azd/pkg/tools/script.go @@ -15,9 +15,6 @@ type ExecOptions struct { Interactive *bool StdOut io.Writer UserPwsh string - // If true, azd won't try to use alternative shell to execute the command. For example, pwsh won't be tried with - // powershell (pwsh5) in Windows. - StrictShell bool } // Utility to easily execute a bash script across platforms From 4c7e9f30fbb0a1c3f28476c64788723301026b10 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 11 Aug 2025 19:26:45 +0000 Subject: [PATCH 10/15] remove schema changes --- schemas/alpha/azure.yaml.json | 6 ------ schemas/v1.0/azure.yaml.json | 6 ------ 2 files changed, 12 deletions(-) diff --git a/schemas/alpha/azure.yaml.json b/schemas/alpha/azure.yaml.json index f58646aafd6..49981e1e90c 100644 --- a/schemas/alpha/azure.yaml.json +++ b/schemas/alpha/azure.yaml.json @@ -744,12 +744,6 @@ "title": "Required. The inline script or relative path of your scripts from the project or service path", "description": "When specifying an inline script you also must specify the `shell` to use. This is automatically inferred when using paths." }, - "strictShell": { - "type": "boolean", - "default": false, - "title": "Whether to use strict shell mode", - "description": "Optional. When set to true will only allow the specified shell to be used. (Default: false). Some shells like pwsh will try powershell 7 first and powershell 5 (in Windows) as an alternative in non strict mode." - }, "continueOnError": { "type": "boolean", "default": false, diff --git a/schemas/v1.0/azure.yaml.json b/schemas/v1.0/azure.yaml.json index 0b6e2cfd7a6..52eafc80307 100644 --- a/schemas/v1.0/azure.yaml.json +++ b/schemas/v1.0/azure.yaml.json @@ -704,12 +704,6 @@ "title": "Required. The inline script or relative path of your scripts from the project or service path", "description": "When specifying an inline script you also must specify the `shell` to use. This is automatically inferred when using paths." }, - "strictShell": { - "type": "boolean", - "default": false, - "title": "Whether to use strict shell mode", - "description": "Optional. When set to true will only allow the specified shell to be used. (Default: false). Some shells like pwsh will try powershell 7 first and powershell 5 (in Windows) as an alternative in non strict mode." - }, "continueOnError": { "type": "boolean", "default": false, From 626bed2d170bbc3f60ddd172cbe5f469e3a36624 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 11 Aug 2025 20:49:32 +0000 Subject: [PATCH 11/15] remove comment --- cli/azd/pkg/tools/powershell/powershell.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 7e12526e187..14bc3c5a3fd 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -67,7 +67,6 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to } } - // non-strict mode, check for alternative shell powershell 5 options.UserPwsh = "powershell" if err := bs.checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ From 9bcb3e4745871289689ba08f8b2988dbab40739b Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 11 Aug 2025 21:58:06 -0700 Subject: [PATCH 12/15] Apply suggestion from @JeffreyCA Co-authored-by: JeffreyCA --- cli/azd/pkg/tools/powershell/powershell.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 14bc3c5a3fd..130c60a00af 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -72,7 +72,7 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, Suggestion: fmt.Sprintf( - "Make sure pwsh (Powershell 7) or powershell (Powershell 5) is installed on your system, visit %s", + "Make sure pwsh (PowerShell 7) or powershell (PowerShell 5) is installed on your system, visit %s", output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), } } From 3cd97726264c9626785b120c0e8b4c65d68fbc37 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 11 Aug 2025 21:58:17 -0700 Subject: [PATCH 13/15] Apply suggestion from @JeffreyCA Co-authored-by: JeffreyCA --- cli/azd/pkg/tools/powershell/powershell.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 130c60a00af..96516dda3b0 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -96,9 +96,9 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to if noPwshError != nil { err = &internal.ErrorWithSuggestion{ Err: err, - Suggestion: "pwsh (Powershell 7) was not found and powershell (Powershell 5) was automatically used " + + Suggestion: "pwsh (PowerShell 7) was not found and powershell (PowerShell 5) was automatically used " + "instead. You can try installing pwsh and trying again in case this script is not compatible with " + - "Powershell 5.", + "PowerShell 5.", } } } From 3b0c1ec0b588450d81c83e7ba0a127787ae396db Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Tue, 12 Aug 2025 05:04:29 +0000 Subject: [PATCH 14/15] use link for fallback error --- cli/azd/pkg/tools/powershell/powershell.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 14bc3c5a3fd..56beb474a9b 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -96,9 +96,10 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to if noPwshError != nil { err = &internal.ErrorWithSuggestion{ Err: err, - Suggestion: "pwsh (Powershell 7) was not found and powershell (Powershell 5) was automatically used " + - "instead. You can try installing pwsh and trying again in case this script is not compatible with " + - "Powershell 5.", + Suggestion: fmt.Sprintf("pwsh (Powershell 7) was not found and powershell (Powershell 5) was automatically"+ + " used instead. You can try installing pwsh and trying again in case this script is not compatible "+ + "with Powershell 5. See: %s", + output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), } } } From f95a39f4ca2d2a3a97b2b575ffde522db2d58ed3 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 11 Aug 2025 22:14:53 -0700 Subject: [PATCH 15/15] fix not-installed test --- cli/azd/pkg/tools/powershell/powershell_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell_test.go b/cli/azd/pkg/tools/powershell/powershell_test.go index 9dc4adeb9d1..8f049aa17af 100644 --- a/cli/azd/pkg/tools/powershell/powershell_test.go +++ b/cli/azd/pkg/tools/powershell/powershell_test.go @@ -139,7 +139,13 @@ func Test_Powershell_Execute(t *testing.T) { t.Run("NoPowerShellInstalled", func(t *testing.T) { mockContext := mocks.NewMockContext(context.Background()) - PowershellScript := NewPowershellScript(mockContext.CommandRunner, workingDir, env) + PowershellScript := NewPowershellScriptWithMockCheckPath( + mockContext.CommandRunner, + workingDir, + env, + func(options tools.ExecOptions) error { + return fmt.Errorf("failed to find PowerShell executable") + }) _, err := PowershellScript.Execute( *mockContext.Context, scriptPath,