How to run vswhere on powershell - visual-studio

I found this script for finding where msbuild.exe is, but it's not working. When I run it, it says "A parameter cannot be found that matches parameter name 'latest'" https://github.com/microsoft/vswhere/wiki/Find-MSBuild
Here is my script:
$path = %ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1
if ($path) {
& $path $args
}
Does anyone know what I'm doing wrong? I tried encapsulating the path in quotes, and putting it into a variable and tried using Invoke-Expression but this also did not work. In bash all I would need to do is encapsulate the command with ` and it would work.
Invoke-Expression -Command "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
x86: The term 'x86' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
This also does not work

Try this, here I am expliclty creating a list of arguments in seperate strings.
$path = "$(${env:CommonProgramFiles(x86)})\Microsoft Visual Studio\Installer\vswhere.exe"
$args = "-latest", "-requires Microsoft.Component.MSBuild", "-find MSBuild\**\Bin\MSBuild.exe"
if ($path) {
Start-Process $path -ArgumentList $args
}
I am unable to test as i dont have Visual Studio.

This is "small" script which I wrote.
It will:
download latest recomended Nuget.exe in same directory where this PS script will be saved.
download/install "vswhere" nuget (in script directory)
will find latest "MSBuild.exe" path
$latestRecomendedUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$currentScriptDir = "$PSScriptRoot"
$nuget = Join-Path "$currentScriptDir" "nuget.exe"
if (!(Test-Path "$nuget" -pathType Leaf)) {
# download if not exists
Invoke-WebRequest "$latestRecomendedUrl" -OutFile "$nuget" | Out-Null
}
$vswhereExePartFromScriptDir = (Get-ChildItem -Path "$currentScriptDir" -Filter "vswhere.exe" -Recurse -Name)
$vswhereExe = Join-Path "$currentScriptDir" "$vswhereExePartFromScriptDir"
if ([string]::IsNullOrEmpty($vswhereExePartFromScriptDir)) {
try {
$args = "install", "-x", "-o", "$currentScriptDir", "vswhere"
&"$nuget" $args
Write-Host "Nuget was successfully downloaded."
}
finally {
$vswhereExePartFromScriptDir = (Get-ChildItem -Path "$currentScriptDir" -Filter "vswhere.exe" -Recurse -Name)
$vswhereExe = Join-Path "$currentScriptDir" "$vswhereExePartFromScriptDir"
# in my case install throws error #"nuget.exe : The requested operation cannot be performed on a file with a user-mapped section open."#
# but it download vswhere package successfully - so throw error only if it does not exist
if ([string]::IsNullOrEmpty($vswhereExePartFromScriptDir)) {
Write-Error "Failed to download 'vswhere.exe'."
}
else {
Write-Host "vswhere executable: ""$vswhereExe"""
}
}
}
else {
Write-Host "vswhere executable: ""$vswhereExe"""
}
$args = "-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild", "-find", "MSBuild\**\Bin\MSBuild.exe"
$msbuildExe = $(& "$vswhereExe" $args) | select-object -first 1
Write-Host "MSBuild exeutable: ""$msbuildExe"""
Output:
Feeds used:
https://api.nuget.org/v3/index.json
C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\
Installing package 'vswhere' to 'G:\scripts\ps'.
CACHE https://api.nuget.org/v3/registration5-gz-semver2/vswhere/index.json
Attempting to gather dependency information for package 'vswhere.3.1.1' with respect to project 'G:\scripts\ps', targeting 'Any, Version=
v0.0'
Gathering dependency information took 15 ms
Attempting to resolve dependencies for package 'vswhere.3.1.1' with DependencyBehavior 'Lowest'
Resolving dependency information took 0 ms
Resolving actions to install package 'vswhere.3.1.1'
Resolved actions to install package 'vswhere.3.1.1'
Retrieving package 'vswhere 3.1.1' from 'nuget.org'.
Adding package 'vswhere.3.1.1' to folder 'G:\scripts\ps'
WARNING: Install failed. Rolling back...
Executing nuget actions took 483 ms
nuget.exe : The requested operation cannot be performed on a file with a user-mapped section open.
At G:\scripts\ps\Get-MsBuildDir.ps1:21 char:9
+ &"$nuget" $args
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The requested o...d section open.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Nuget was successfully downloaded.
vswhere executable: "G:\scripts\ps\vswhere\tools\vswhere.exe"
MSBuild exeutable: "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe"

Related

How to get list of packages of a particular Visual Studio solution with nuget.exe command line

I would like to get a list of packages of my Visual Studio solution after I ran the nuget restore command.
How can I do it from command line or Powershell (oustide Visual Studio)?
You could run following PowerShell script to list all installed packages in your solution. Please modify the $SOLUTIONROOT as your solution path.
#This will be the root folder of all your solutions - we will search all children of this folder
$SOLUTIONROOT = "D:\Visual Studio 2015 Project\SO Case Sample\PackageSource"
Function ListAllPackages ($BaseDirectory)
{
Write-Host "Starting Package List - This may take a few minutes ..."
$PACKAGECONFIGS = Get-ChildItem -Recurse -Force $BaseDirectory -ErrorAction SilentlyContinue |
Where-Object { ($_.PSIsContainer -eq $false) -and ( $_.Name -eq "packages.config")}
ForEach($PACKAGECONFIG in $PACKAGECONFIGS)
{
$path = $PACKAGECONFIG.FullName
$xml = [xml]$packages = Get-Content $path
foreach($package in $packages.packages.package)
{
Write-Host $package.id
}
}
}
ListAllPackages $SOLUTIONROOT
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

Why is AWS-ConfigureWindowsUpdate SSM Run Command Failing?

Enabling or Disabling Windows Updates with SSM Run Command
AWS-ConfigureWindowsUpdate
When running the command, it downloads a PowerShell script to my EC2 at "C:\ProgramData\Amazon\Ec2Config\Downloads\aws_psModule\" with a random name
When the PowerShell script executes, it downloads "Amazon.ConfigureWindowsUpdate-1.2.zip" to "%Temp%" and then unzips it to "%WinDir%\System32\WindowsPowerShell\v1.0\Modules"
The script looks to be failing at Line 32 with the .CopyHere function where it is unzipping
Pastebin of Powershell Script: 1b3hh3oy.k51.ps1
(New-Object -Com Shell.Application).namespace($powerShellModuleLocation).CopyHere((New-Object -Com Shell.Application).namespace($tempLocation).Items(), 16)
Output:
Obtaining instance region from instance metadata.
Downloading ConfigureWindowsUpdate PowerShell module from S3.
Verifying SHA 256 of the ConfigureWindowsUpdate PowerShell module zip file.
ExtractingConfigureWindowsUpdate zip file contents to the Windows PowerShell module folder.
--------------ERROR-------------- C:\ProgramData\Amazon\Ec2Config\Downloads\aws_psModule\1b3hh3oy.k51.ps1 :
Exception thrown while downloading ConfigureWindowsUpdate
PowerShell module with message: Exception has been thrown by the target of an invocation.
At line:1 char:1
+ . 'C:\ProgramData\Amazon\Ec2Config\Downloads\aws_psModule\1b3hh3oy.k51.ps1' ; ex ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,1b3hh3oy.k51.ps1
Other Details:
- I have micro EC2 of Windows Server Core 2012 R2 running
- I have successfully used AWS-RunPowerShellScript command a few times from AWS Console
- I ran AWS-ConfigureWindowsUpdate through the AWS Console and it fails
- I remote connected to server and ran the powershell script with administrator privileges and get same error
You are correct, the exception is occurring on the call out to the Shell namespace for extracting the cmdlet payload. The COM namespace for Shell access is not included in the Core release so the ConfigureWindowsUpdate script fails when extracting the cmdlet.
Currently there is a workaround available for Windows Server Core AMIs and a more complete fix is currently being investigated. The workaround involves creating a custom ConfigureWindowsUpdate document with a tweak to fix the extraction process.
Below is a function that would replace the call to
(New-Object -Com Shell.Application).namespace($powerShellModuleLocation).CopyHere((New-Object -Com Shell.Application).namespace($tempLocation).Items(), 16)
function ExtractZipCoreOs($zipFilePath, $destPath) {
try
{
[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
$zip = [System.IO.Compression.ZipFile]::OpenRead($zipFilePath)
foreach ($item in $zip.Entries) {
$extractedPath = Join-Path $destPath $item.FullName
if ($item.Length -eq 0) {
if ((Test-Path $extractedPath) -eq 0) {
mkdir $extractedPath | Out-Null
}
} else {
$fileParent = Split-Path $extractedPath
if ((Test-Path $fileParent) -eq 0) {
mkdir $fileParent | Out-Null
}
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($item,(Join-Path -Path $powerShellModuleLocation -ChildPath $item.FullName), $true)
}
}
}
catch
{
throw "Error encountered when extracting ConfigureWindowsUpdate zip file.`n$($_.Exception.Message)"
}
finally
{
$zip.Dispose()
}
}

Publishing DacPacs in Visual Studio 2013

I have an SSDT database project in Visual Studio 2013. This is used as the "answer sheet" when publishing database updates to a database in the other environments. I recently came across Jamie Thompson's blog article on DacPacs, where he writes a great summary on what DacPacs are, and how to use them.
Now, say I have the following scenario:
The SSDT project in VS2013, which is version 1.0.33
A database in my Dev environment, which is version 1.0.32
A database in my S-test environment, whic is version 1.0.31
According to Jamie, publishing databases changes using DacPacs is idempotent, i.e. I can publish the DacPac from the SSDT project in bullet 1 to the database in bullet 3, and it will get all the changes done to the database in both version 1.0.32 and 1.033 since the DacPac contains information about the entire DB schema (which then also should include changes done in version 1.0.32).
Is this a correct understanding of how publishing a DacPac works?
Yes, once you defined your model in a DACPAC in a declarative way, you can then deploy your model to any target environment with whatever version of you database.
The engine will automatically generate the proper change scripts according to the target.
You can deploy (publish) your model from Visual Studio or from command line using the SqlPackage.exe utility. Here an example of a PowerShell script that use SqlPackage.exe and a Publish Profile file. You can choose to publish directly or generate the change script (set the $action variable). The DACPAC file and the Publish Profile file have to be in the same folder of the ps file. A log file will be generated:
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
####################################
$action = 'Publish' #Only generate script: 'Script'; Publish directly: 'Publish'
$databaseName = 'Test'
$serverName = 'localhost'
$dacpacPath = Join-Path $scriptPath '.\Test\bin\Debug\Test.dacpac'
$publishProfilePath = Join-Path $scriptPath '.\Test\Scripts\Publish\Test.publish.xml'
$outputChangeScriptPath = Join-Path $scriptPath 'TestDeploymentScript.sql'
$logPath = Join-Path $scriptPath 'TestDeployment.log'
####################################
$sqlPackageExe = 'C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe'
if ($action.ToUpper() -eq 'SCRIPT')
{
Write-Host '********************************' | Tee-Object -File "$logPath"
Write-Host '* Database Objects Scripting *' | Tee-Object -File "$logPath"
Write-Host '********************************' | Tee-Object -File "$logPath"
$args = "/Action:Script /TargetDatabaseName:$databaseName /TargetServerName:$serverName " +
"/SourceFile:""$dacpacPath"" /Profile:""$publishProfilePath"" /OutputPath:""$outputChangeScriptPath"" "
$command = "& ""{0}"" {1}" -F $sqlPackageExe, $args
Invoke-Expression $command | Tee-Object -File "$logPath"
if($LASTEXITCODE -ne 0)
{
$commandExitCode = $LASTEXITCODE
$Error[0] | Tee-Object -File $outputChangeScriptPath
return $commandExitCode
}
}
if ($action.ToUpper() -eq 'PUBLISH')
{
# DWH
Write-Host '*********************************' | Tee-Object -File "$logPath"
Write-Host '* Database Objects Deployment *' | Tee-Object -File "$logPath"
Write-Host '*********************************' | Tee-Object -File "$logPath"
$args = "/Action:Publish /TargetDatabaseName:$databaseName /TargetServerName:$serverName " +
"/SourceFile:""$dacpacPath"" /Profile:""$publishProfilePath"" "
$command = "& ""{0}"" {1}" -F $sqlPackageExe, $args
Invoke-Expression $command | Tee-Object -File "$logPath"
if($LASTEXITCODE -ne 0)
{
$commandExitCode = $LASTEXITCODE
$Error[0] | Tee-Object -File $outputChangeScriptPath
return $commandExitCode
}
}

Expected Identifier Code Error when running script?

I got this script off the technet website, but I get an error when I try to execute it on my Windows 7 machine. I am completely new to scripting, but I wonder if this was made for an older OS and needs a bit of changing for Windows 7? I'm quite sure the guy who wrote it up tested it.
I get the Windows Script Host Error as follows:
Line: 1
Char: 10
Error: Expected Identifier
Code: 800A03F2
Source: Microsoft VBScript compilation error.
Here's the script:
Function New-BackUpFolder($destinationFolder)
{
$dte = get-date
$dte = $dte.tostring() -replace "[:\s/]", "."
$backUpPath = "$destinationFolder" + $dte
$null = New-Item -path $backUpPath -itemType directory
New-Backup $dataFolder $backUpPath $backUpInterval
} #end New-BackUpFolder
Function New-Backup($dataFolder,$backUpPath,$backUpInterval)
{
"backing up $dataFolder... check $backUppath for your files"
Get-Childitem -path $dataFolder -recurse |
Where-Object { $_.LastWriteTime -ge (get-date).addDays(-$backUpInterval) } |
Foreach-Object { copy-item -path $_.FullName -destination $backUpPath -force }
} #end New-BackUp
# *** entry point to script ***
$backUpInterval = 1
$dataFolder = "C:\fso"
$destinationFolder = "C:\BU\"
New-BackupFolder $destinationFolder
that's actually Powershell and not VB script. You need to run the code inside Powershell for this to work.
This link looks pretty good for a brief introduction if you haven't done PS before.
http://www.abstrys.com/files/BeginningPowershellScripting.html

Determine local path of a TFS Workspace via tf.exe

I am using a batch script for getting the latest version of specific projects. This script only runs tf.exe and gets the latest version of some Binaries. Everything works fine, but I would like to change the attrib of the downloaded files to be writeable (by deafult these files are read-only). For that I want to determine the local path of the files and use the attrib-command from batch.
tf.exe workfold [Workspace] shows me the local path in some kind of listing but it would be easier, if it only shows me what I want so I can use the prompt. Until now the it looks like this:
tf.exe workfold [Workspace]
=======================================
Arbeitsbereich: XYZ-xxxxxx (Username)
Auflistung: TFS-URL
[Workspace]: C:\xxx\TFS\xxx
Is it possible to determine only the local path mapping of a TFS Workspace so that I can use the prompt for the attrib-command without parsing?
What about the following (crude!!!) concept?
function Get-TfsWorkfold([string]$TfsCollection, [string]$TfsWorkspace)
{
$TfExePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe"
Write-Output "Getting workfold for '$TfsCollection'->'$TfsWorkspace'..."
Push-Location $LocalPath
& "$TfExePath" workfold /collection:$TfsCollection /workspace:$TfsWorkspace
}
function Handle-Path()
{
param([Parameter(ValueFromPipeline=$true,Position=0)] [string] $line)
$startIndex = $line.IndexOf(': ') + 2;
$correctedLine = $line.subString($startIndex, $line.length - $startIndex - 1);
Write-Output $correctedLine;
Get-ChildItem $correctedLine
}
Get-TfsWorkfold "{serverAndcollection}" "{workspace}" > c:\temp\test.txt
Select-String c:\temp\test.txt -pattern:': ' | Select-Object Line | Handle-Path
The last line in Handle-Path is the example which you can rewirte with whatever you want to. It is PowerShell but it should work as you want.
Replace {serverAndcollection} and {workspace}.
Real men do it in one line
powershell -command "& {tf workfold | Select-String -pattern:' $' -SimpleMatch | Select-Object Line | ForEach-Object {$startIndex = $_.Line.IndexOf(': ') + 2; $_.Line.subString($startIndex, $_.Line.length - $startIndex - 1)}}"
Current answer will only return one last path if there are many.
You can also do it without any string manipulation, with calls to TF.exe. I have wrapped that in PowerShell scripts, so you get the following:
function Add-TfsTypes
{
# NOTE: Not all of the below are needed, but these are all the assemblies we load at the moment. Please note that especially NewtonSoft dll MUST be loaded first!
$PathToAssemblies = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
Add-Type -Path "$PathToAssemblies\NewtonSoft.Json.dll"
Add-Type -Path "$PathToAssemblies\System.Net.http.formatting.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.Client.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.Common.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.VersionControl.Client.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
}
function Get-TfsServerPathFromLocalPath {
param(
[parameter(Mandatory=$true)][string]$LocalPath,
[switch]$LoadTfsTypes
)
if ($LoadTfsTypes) {
Add-TfsTypes # Loads dlls
}
$workspaceInfo = [Microsoft.TeamFoundation.VersionControl.Client.Workstation]::Current.GetLocalWorkspaceInfo($LocalPath)
$server = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection $workspaceInfo.ServerUri
$workspace = $workspaceInfo.GetWorkspace($server)
return $workspace.GetServerItemForLocalItem($LocalPath)
}
The above method can then be called like this:
$serverFolderPath = Get-TfsServerPathFromLocalPath $folderPath -LoadTfsTypes
$anotherServerPath = Get-TfsServerPathFromLocalPath $anotherItemToTestPathOn

Resources