2

I am trying to create a Visual Studio Developer Powershell profile for Visual Studio Code. Here is what I tried:

"Developer Powershell": {
    "path": "pwsh",
    "args": [
        "-noe",
        "-c",
        "\"&{$vsPath = &(Join-Path ${env:ProgramFiles(x86)} '\\Microsoft Visual Studio\\Installer\\vswhere.exe') -property installationpath; Import-Module (Join-Path $vsPath 'Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll'); Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation}\""
    ],
    "icon": "terminal-powershell"
}

I copied and pasted the arguments from my Windows Terminal profile, which works:

{
    "commandline": "pwsh.exe -noe -c \"&{$vsPath = &(Join-Path ${env:ProgramFiles(x86)} '\\Microsoft Visual Studio\\Installer\\vswhere.exe') -property installationpath; Import-Module (Join-Path $vsPath 'Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll'); Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation}\"",
    "icon": "C:\\powershell\\713\\assets\\Square44x44Logo.png",
    "name": "Developer PowerShell",
    "startingDirectory": "%USERPROFILE%",
}

However, terminal output looks like this:

&{ = &(Join-Path C:\Program Files (x86) '\Microsoft Visual Studio\Installer\vswhere.exe') -property installationpath; Import-Module (Join-Path  'Common7\Tools\Microsoft.VisualStudio.DevShell.dll'); Enter-VsDevShell -VsInstallPath  -SkipAutomaticLocation}
  StaggoSTD   

Note: I am using Oh My Posh here. That's why the last line looks weird without a nerd font.

So how do I make the Developer Powershell work? I tried using powershell instead of pwsh and removing the escaped double quotes but that did nothing.

4 Answers 4

4

tl;dr

The following - which can be used as a property inside the "terminal.integrated.profiles.windows" property of your settings.json file in order to define a terminal profile named Developer PowerShell - fixes the problems with your original attempt.

"Developer Powershell": {
  "path": "pwsh",
  "args": [
    "-noexit",
    "-c",
    "$vsPath = & \"${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe\" -property installationpath; Import-Module \"$vsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll\"; Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation"
  ],
  "icon": "terminal-powershell"
}

Note:

  • Do not use this shell profile as your default profile - unless you've also configured explicitly configured an automation shell to be used for tasks and debugging, via a "terminal.integrated.automationProfile.windows" property - see Terminal Profiles.

    • The problem is that automation operations pass additional command(s) to execute, and Visual Studio Code passes them by blindly appending -Command <command> to the shell command line (/d /c <command> if cmd.exe is the shell), which breaks if the shell profile itself already defines commands (rather than mere options) to execute on startup.
  • Given that the assumption that environment variable ProgramFiles(x86) expands to C:\Program Files (x86) is pretty safe (though it is possible for it to expand to a different path on unusually configured systems), your own solution, which avoids use of ${env:ProgramFiles(x86)}, should typically work just fine.

  • The solution above still uses ${env:ProgramFiles(x86)}, and makes it work by including it in an embedded expandable string (\"...\") that constructs the path strings directly, not via Join-Path. This not only simplifies the command, it avoids one of the original problems: use of ${env:ProgramFiles(x86)} outside of a (PowerShell) string literal; the next section explains why that is a problem.

  • Another simplification is the use of / as the path separator - which PowerShell accepts interchangeably with \ - which doesn't require escaping inside a JSON string.


Background information:

  • Since you're passing the PowerShell CLI arguments as an array, via the "args" property, you should not additionally enclose the string passed to the -c parameter in an embedded, escaped "..." string (\"...\"), because with "args" it is Visual Studio Code that performs any necessary escaping when stitching together the command line ultimately invoked behind the scenes.

    • If you do use enclosure in \"...\", PowerShell ultimately sees your commands as a single, expandable string literal, whose expanded value is implicitly echoed - that is the symptom you saw; in simple terms, PowerShell ended up executing something like "$vsPath = ...", which attempts to expand (interpolate) variable $vsPath, and since no such variable exists, it evaluates to the empty string, causing the following verbatim string to be output: = ...

    • To put it differently: With "args", you needn't worry about quoting for the command line: You simply formulate what each argument should be verbatim, and let VS Code do the escaping - however, there is JSON-related escaping you do need to perform, and there's the pitfall of VS Code rather than PowerShell potentially up-front expanding variable references such as ${env:ProgramFiles(x86)}:

      • JSON-escaping: Escape embedded " as \", \ as \\; note that the pain of the latter can be eased by using / as the path separator instead, which PowerShell accepts interchangeably with \.
      • VS Code up-front variable expansion pitfall:
        • VS Code only considers references of the form ${someName} and ${someNameSpace:someName} candidates for up-front expansion - not also $someName and $someNameSpace:someName
        • Therefore, typical PowerShell variable references such as $vsPath or $env:USERNAME are not subject to potential up-front expansion, but it may happen in cases where enclosure in {...} is syntactically required in PowerShell, due to a nonstandard name such as ${env:ProgramFiles(x86)}.
        • With the env: namespace, specifically, up-front expansion always happens (as with command:, config:, and input:, but these aren't typically defined in PowerShell): VS Code expands a reference such as ${env:ProgramFiles(x86)} to the verbatim value of that environment variable. That is, ${env:ProgramFiles(x86)} is replaced with verbatim C:\Program Files (x86) in the command string before PowerShell sees the string, and the lack of quoting around the value then causes a syntax error.
        • While this up-front expansion cannot be suppressed, a simple solution is to enclose the reference in quotes: '${env:ProgramFiles(x86)}' or \"${env:ProgramFiles(x86)}\". Note that this assumes that the value itself contains no ' or ", respectively; in cases where this assumption cannot be made, use the following workaround:
          (Get-Content 'env:ProgramFiles(x86)')
  • As an aside: It is unnecessary to enclose statements passed to -c (-Command) in & { ... } - just use ... (the statements) directly.

    • Older versions of the CLI documentation erroneously suggested that & { ... } is required, but this has since been corrected.
Sign up to request clarification or add additional context in comments.

5 Comments

For some reason I had to use "path": "powershell" instead of pwsh (Using Windows 10 21H2)
@d2weber: powershell.exe is the CLI of Windows PowerShell, the legacy PowerShell edition that ships with Windows (whose latest and last version is v5.1). pwsh.exe is the CLI of PowerShell (Core) 7+, the modern, install-on-demand, cross-platform edition.
thanks for the explaination. I installed pwsh. But the problem why I tried to use pwsh instead of powershell didn't resolve: I configured the profile as default. Running a task with "type": "shell" results in the following error: x86: The term 'x86' is not recognized as a name of a cmdlet, function, script file, or executable program. Seems like VSCode uses a different kind of escaping in that case. As a workaround I could set terminal.integrated.automationShell
@d2weber - He is basically saying that profile doesn't work because the \" sequences aren't working as you are intending in vscode. The installation path isn't actually being assigned to the variable.
@elikakohen, the \" sequences work correctly, but I missed the task angle in d2weber's comment: indeed, you cannot use the shell profile in this answer for automation purposes with "type": "shell", because that will cause the "command" value to blindly be appended as -Command <command> to the command line that results from the shell profile's own definition, resulting in a broken command.
2

I have figured it out. Firstly, I would like to give credit to mklement0, who has submitted an answer on simplifying the arguments, however it has been deleted. Using this, I replaced ${env:ProgramFiles(x86)} with 'C:\\Program Files (x86)' and everything went smoothly. To check if everything was properly working, I ran gcm fsi, which told me that F# Interative was there, even though it is not on my normal PATH. Here is my profile now for anybody who wants Developer Powershell in VSCode:

"Developer Powershell": {
    "path": "pwsh",
    "args": [
        "-noe",
        "-c",
        "$vsPath = &(Join-Path 'C:\\Program Files (x86)' '\\Microsoft Visual Studio\\Installer\\vswhere.exe') -property installationpath; Import-Module (Join-Path $vsPath 'Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll'); Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation"
    ],
    "icon": "terminal-powershell"
}

As for why the environment variable was there, I copied and pasted from a Microsoft blog. Because it worked for Windows Terminal, I thought it worked for VSCode too. However, every other place I looked for a Developer Powershell profile, the environment variable wasn't used. Later on, I found out that I can just copy from the shortcut at C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2019\Visual Studio Tools.

1 Comment

Nice - using a quoted (PowerShell) string literal in lieu of ${env:ProgramFiles(x86)} indeed avoids one of the original problems. Please note that I've since undeleted my answer; please see the comments there.
1

There are a couple of challenges to customizing PowerShell Integrated Terminal Profiles to work together with Microsoft.VisualStudio.DevShell.dll:

  • Use the latest version of PowerShell, (not PowerShell.exe, but pwsh.exe).
  • There is a "defect" in Visual Studio Code that injects a -Command when using the "terminal.integrated.automationProfile.windows" in settings.json. So, don't use it. Instead...
  • Directly configure Visual Studio Code's Task with Microsoft.VisualStudio.DevShell.dll. Kind of a pain, but it will have to do until the "terminal.integrated.automationProfile.windows" works as expected.

Configuring the integrated terminal profile:

    "terminal.integrated.defaultProfile.windows": "Developer PowerShell",
    "terminal.integrated.shellIntegration.enabled": false,
    "terminal.integrated.shellIntegration.decorationsEnabled": "both",
    "terminal.integrated.profiles.windows": {

    "PowerShell": {
        "source": "PowerShell",
        "icon": "terminal-powershell"
    },
    "Command Prompt": {
        "path": [
            "${env:windir}\\Sysnative\\cmd.exe",
            "${env:windir}\\System32\\cmd.exe"
        ],
        "args": [],
        "icon": "terminal-cmd"
    },
    "Git Bash": {
        "source": "Git Bash"
    },
    "Developer PowerShell": {
        "path": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
        "icon": "terminal-powershell",
        "args": [
            "-noexit",
            "-Command",
            "Import-Module 'C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/Common7/Tools/Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell -VsInstallPath 'C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools' -SkipAutomaticLocation;"
        ]
        
    }

Configuring Microsoft.VisualStudio.DevShell.dll for Specific Tasks:

{
    "version": "2.0.0",
    "tasks": [

 {
            "type": "shell",
            "label": "MyApp.API - Debug",
            "detail": "Clean and Precompile API.",
            "dependsOn": "MyApp.API - Clean",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "clear": false,
                "echo": true,
                "panel": "shared"
            },
            "problemMatcher": [
                "$msCompile"
            ],
            "options": {
                "shell": {
                    "executable": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
                    "args": [
                        "-NoProfile",
                        "-ExecutionPolicy",
                        "Bypass",
                        "-Command",
                        "Import-Module 'C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/Common7/Tools/Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell -VsInstallPath 'C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools' -SkipAutomaticLocation;"
                    ]
                }
            },
            "command": "cl.exe",
            "args": [
                "/c",
                "/std:c++20",
                "/permissive-",
                "/await",
                "/bigobj",
                "/utf-8",
                "/MTd",
                "/sdl",
                "/Gr",
                "/GL",
                "/Zo",
                "/Z7",
                "/EHsc",
                "/FC",
                "/Fd:",
                "Build/Debug/pch.pdb",
                "/Fo:",
                "Build/Debug/",
                "Include/pch.cpp",
                "/Ycpch.h",
                "/FpBuild/Debug/pch.pch"
            ]
        }
    ]
}
}

Comments

0

Update on what mklement0 previously said (March 2023)

This worked for me. Modify last argument according to where your Powershell Developer is. I used Windows Terminal Settings to look up the proper path and args for my system.

        "Developer PowerShell": {
        "path": "powershell",
        "color": "terminal.ansiMagenta",
        "args": [
            "-NoExit",
            "-Command",
            "$vsPath = & \"${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe\" -property installationpath; Import-Module \"${env:ProgramFiles(x86)}/Microsoft Visual Studio/2022/BuildTools/Common7/Tools/Microsoft.VisualStudio.DevShell.dll\"; Enter-VsDevShell cc62515d -SkipAutomaticLocation -DevCmdArguments '-arch=x64 -host_arch=x64'"
            ]
    },

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.