Passing parameters to Boxstarter via Install-BoxstarterPackage - powershell

When invoking a BoxStarter package, I use a powershell line such as:
Install-BoxstarterPackage -Package $script
I would like to pass some command line arguments into the package, but this does not seem to be supported.
Some relevant discussion:
https://github.com/mwrock/boxstarter/issues/5
An issue about a fix that does not seem to exist:
https://github.com/mwrock/boxstarter/issues/66
I ended up "cheating" and setting an environment variable before the package invocation like so:
$env:BoxstarterFoo = "Some Value"
Install-BoxstarterPackage -Package $script
Which makes $env:BoxstarterFoo available from within the package. This does not seem optimal. Is there a better way to pass information from an installation script into a boxstarter install right now, or is a workaround such as using environment variables required?

I'm afraid that is likely the best workaround currently available.

Related

How to make parameter name suggestions work?

I'm trying to create a powershell module to store some reusable utility functions. I created script module PsUtils.psm1 and script module manifest PsUtils.psd1 (used these docs). My problem is that when I import this module in another script Visual Code does not suggest parameters names. Here's a screenshot:
When I hover cursor over the function I only get this:
PsUtils.psm1
function Get-Filelist {
Param(
[Parameter(Mandatory=$true)]
[string[]]
$DirectoryPath
)
Write-Host "DIR PATH: $DirectoryPath"
}
PsUtils.psd1 (excerpt)
...
FunctionsToExport = '*'
I have Powershell extension installed. Do I need to install anything else to make the suggestions work? What am I missing?
Generally speaking, only auto-loading modules - i.e., those in one of the directories listed in environment variable $env:PSModulePath - are automatically discovered.
As of version v2022.7.2 of the PowerShell extension, the underlying PowerShell editor services make no attempt to infer from the current source-code file what modules in nonstandard directories are being imported via source code in that file, whether via Import-Module or using module
Doing so would be the prerequisite for discovering the commands exported by the modules being imported.
Doing so robustly sounds virtually impossible to do with the static analysis that the editor services are limited to performing, although it could work in simple cases; if I were to guess, such a feature request wouldn't be entertained, but you can always ask.
Workarounds:
Once you have imported a given module from a nonstandard location into the current session - either manually via the PIC (PowerShell Integrated Console) or by running your script (assuming the Import-Module call succeeds), the editor will provide IntelliSense for its exported commands from that point on, so your options are (use one of them):
Run your script in the debugger at least once before you start editing. You can place a breakpoint right after the Import-Module call and abort the run afterwards - the only prerequisite is that the file must be syntactically valid.
Run your Import-Module command manually in the PIC, replacing $PSScriptRoot with your script file's directory path.
Note: It is tempting to place the cursor on the Import-Module line in the script in order to use F8 to run just this statement, but, as of v2022.7.2, this won't work in your case, because $PSScriptRoot is only valid in the context of running an entire script.
GitHub issue #633 suggests adding special support for $PSScriptRoot; while the proposal has been green-lighted, no one has stepped up to implement it since.
(Temporarily) modify the $env:PSModulePath variable to include the path of your script file's directory.
The most convenient way to do that is via the $PROFILE file that is specific to the PowerShell extension, which you can open for editing with psedit $PROFILE from the PIC.
Note: Make sure that profile loading is enabled in the PowerShell extension's settings.
E.g., if your directory path is /path/to/my/module, add the following:
$env:PSModulePath+="$([IO.Path]::PathSeparator)/path/to/my/module"
The caveat is that all scripts / code that is run in the PIC will see this updated $env:PSModulePath value, so at least hypothetically other code could end up mistakenly importing your module instead of one expected to be in the standard locations.
Note that GitHub issue #880 is an (old) proposal to allow specifying $env:PSModulePath entries as part of the PowerShell extension settings instead.
On a somewhat related note:
Even when a module is auto-discovered / has been imported, IntelliSense only covers its exported commands, whereas while you're developing that module you'd also like to see its private commands. Overcoming this limitation is the subject of GitHub issue #104.

TFS Build and Powershell: how to acces predefined build variables

I have issues with accessing build variables from powershell script for example I want to access Agent.BuildDirectory.
I tried:
$Build.SourcesDirectory
$(Build.SourcesDirectory)
none works.
I know that I can use $Env:TF_BUILD_SOURCESDIRECTORY but not all variables are available this way.
Do you have any suggestions ?
So this is TFS 2017 Update 3. You should be able to do what you are trying to do. Where are you trying to use the variables, as parameter in the build or actually inside a PowerShell script? Those variable can be used as parameters and will be substituted at run time by the agent and passed to scripts, but if you are trying to access them inside a script like $(...) it will not work. You do need to use $env:VARIABLE. All variables in the variables section get converted to environment variables with their name at runtime. So for example if you are running an inline PowerShell like the image bellow, you can use $(..)
or if you want to pass parameters to the a PowerShell script you can also.
But from inside a script you cannot.
You should use $(Agent.BuildDirectory) not $(Agent.SourcesDirectory). Check Agent variables from the link below:
https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#agent-variables
Update:
Adding a screenshot:

Executing powershell command directly in jenkins pipeline

Is it possible to call a PowerShell command directly in the pipelines groovy script? While using custom jobs in Jenkins I am able to call the command with the PowerShell Plugin. But there is no snippet to use this in the groovy script.
I also tried sh() but it seems that this command does not allow multiple lines and comments inside the command.
To call a PowerShell script from the Groovy-Script:
you have to use the bat command.
After that, you have to be sure that the Error Code (errorlevel) variable will be correctly returned (EXIT 1 should resulting in a FAILED job).
Last, to be compatible with the PowerShell-Plugin, you have to be sure that $LastExitCode will be considered.
I have notice that the 'powershell' is now available in pipeline, but since it have several issues I prefer this variant. Still waiting it works stabil. I actually have an issue with the 'dontKillMe' behavior.
Since Jenkins 2.207 with Powershell plugin 1.4, I have replace all my calls with the official powershell pipeline command. I do now recommend to use it.
Note that you must predent \$ErrorActionPreference='Stop'; to your Script if you want it to abort on Write-Error because of an Issue with the powershell plugin.
For that porpuse I have written a little groovy method which could be integrate in any pipeline-script:
def PowerShell(psCmd) {
psCmd=psCmd.replaceAll("%", "%%")
bat "powershell.exe -NonInteractive -ExecutionPolicy Bypass -Command \"\$ErrorActionPreference='Stop';[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$psCmd;EXIT \$global:LastExitCode\""
}
[EDIT] I have added the UTF8 OutputEncoding: works great with Server 2016 and Win10.[/EDIT]
[EDIT] I have added the '%' mask[/EDIT]
In your Pipeline-Script you could then call your Script like this:
stage ('Call Powershell Script')
{
node ('MyWindowsSlave') {
PowerShell(". '.\\disk-usage.ps1'")
}
}
The best thing with that method, is that you may call CmdLet without having to do this in the Script, which is best-praxis.
Call ps1 to define CmdLet, an then call the CmdLet
PowerShell(". '.\\disk-usage.ps1'; du -Verbose")
Do not forget to use withEnv() an then you are better than fully compatible with the Powershell plugin.
postpone your Script with . to be sure your step failed when the script return an error code (should be preferred), use & if you don't care about it.
Calling PowerShell scripts is now supported with powershell step as announced on Jenkins blog.
The documentation mentions it supports multiple lines scripts.
From version 2.28 of Pipeline Nodes and Processes Plugin, we can directly use 'powershell'.
Eg: powershell(". '.Test.ps1'")
You can use the sh command like this:
sh """
echo 'foo'
# bar
echo 'hello'
"""
Comments are supported in here.

Is there a way to access TeamCity system properties in a Powershell script?

I'm trying to set up a new build configuration in TeamCity using the Powershell runner. However, I can't seem to find a way to access the TeamCity System Properties in the build script. I've seen hints that it is possible, but cannot find documentation on how to do it.
I have tried accessing the system properties using Powershell variable syntax, $variable. I have also printed out all variables in memory and see no teamcity variables to use.
Is this possible with the Powershell runner, and if so what is the syntax necessary to get it working?
TeamCity will set up environment variables, such as build.number (you can see a list of these within TeamCity).
In Powershell you can access environment variables using the env "provider", e.g.
$env:PATH
TeamCity variables are accessible by replacing the . with a _, so the build.number variable can be accessed as
$env:build_number
As it says in the TeamCity documentation, the system parameters are passed to the build script runner, but not all build script runners know what to do with them. In the case of the Powershell script runner, when using a script file, they don't propagate down to your scripts.
It's occurred to me to write a psake-optimized build runner that does, but in the meantime you can do one of the following:
explicitly map any of the TeamCity build properties to script parameters using the parameter expansion that's available within the Script Source box. eg .\build.ps1 -someParam:%build.name%
use environment parameters, which can be accessed explicitly within PowerShell using $env:NAME_IN_TEAMCITY syntax, eg $env:TEAMCITY_VERSION, or looped over and pushed into variable scope
access the build properties file that TeamCity makes available during the build. The file is available at $env:TEAMCITY_BUILD_PROPERTIES_FILE, and if you load the XML version it's fairly easy to loop through and push them all into scope (though you do get everything as a string of course). I posted a gist on how to do this (https://gist.github.com/piers7/6432985). Or, if using Psake, modify the script above to return you a hashtable which you can pass directly to Psake's -properties argument.
It is posible. Here is example how to pass system properties into PSake script:
& .\psake.ps1 -parameters #{build_number=%build.number%; personal_build=%build.is.personal%}
If you don't use Psake, you can define your variables like this:
$build_number = %build.number%
The %build.number% part will be replaced with TeamCity provided data. I think, it works only in Source code script input mode.
I created a meta-runner that will pass through System parameters to parameters declared in the Powershell script. It's not perfect (if you put '# in your source it will break) but it works for what I needed, you can find it here: https://gist.github.com/anonymous/ef60ada3f48f0fb25093

Proper use of Invoke-Expression?

I've just recently completed my first nightly build script (first significant anything script, really) in powershell. I seem to have things working well, if not yet robustly (I haven't handled significant error-checking yet), but I found myself falling into an idiom around the Invoke-Expression cmdlet, and I'm wondering if I'm using it properly.
Specifically, I use a series of variables to build up command-lines that I will use to build the solution, then run the solution's unit tests. e.g., something like:
$tmpDir = "C:\Users\<myuser>\Development\Autobuild"
$solutionPath=$tmpDir+"\MyProj\MyProj.sln"
$devenv="C:\Program Files (x86)\Microsoft Visual Studio 10.0\common7\ide\devenv"
$releaseProfile="Release"
$releaseCommandLine="`"$devenv`" `"$solutionPath`" /build `"$releaseProfile`""
This works well enough, $releaseCommandLine contains the command line that I want to execute when I'm done. I then execute it via this line:
$output = Invoke-Expression "& $releaseCommandLine"
Is this the proper way to execute a manually-built command line from a powershell script? I thought initially that Invoke-Command would do it, but I must have been doing something wrong because I couldn't get that working at all for half an hour, and I got this working almost immediately.
I've followed this same pattern a few other times in this same script. Is this a best-practice?
Looks fine to me. Only thing I'd change is to use more Powershell features in place of fragile assumptions. E.g.:
use Join-Path instead of string concatenation
use the Env:\ provider to look up the %programfiles(x86)% dir (or better yet, use the HKML:\ provider to find the path - it's in SOFTWARE\Microsoft\VisualStudio\\InstallDir)
when I have to write a string that contains literal doublequotes and variable expansion, I usually fall back to the syntax below. Personal preference, obviously.
'"{0}" "{1}" /build "{2}"' -f $devenv, $solutionPath, $releaseProfile
In some cases I'd be inclined to use Process.Start() so that I could capture the stdout & stderr streams independently (and maybe even control stdin interactively, depending on the application).
PS - the '&' is not strictly necessary.
I think it is unnecessary to use Invoke-Expression here. I've done this with a lot of build scripts and it usually looks like this:
$vsroot = "$env:ProgramFiles(x86)\Microsoft Visual Studio 9.0"
$devenv = "$vsroot\Common7\IDE\devenv.exe"
$sln = Join-Path <source_root> Source\MyProj\MyProj.sln
& $devenv $sln /build Release
or
& $devenv $sln /build "Release|Any CPU"
Although lately, I have had some troubles with using devenv.exe (mis-behaving add-ins, etc), so now I use msbuild.exe:
$msbuild = 'C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe'
& $msbuild $sln /p:Configuration=Release
Currently MSBuild can handle C#, VB and C++ (invokes vcbuild) but it can't handle solutions with setup & deployment projects in them. However, I have found it to be more reliable than using devenv.exe.
BTW you typically need to invoke other tools (sn.exe, signtool.exe, mt.exe, etc) in a build script that are specific to the version of Visual Studio/.NET you want to build against. So it is usually best to configure your environment variables in the same way that the VS 2008 command prompt does. With the PowerShell Community Extensions installed, you can enable one line in the PSCX profile header to enable this for .NET 3.5/VS 2008 settings:
$Pscx:Preferences["ImportVisualStudioVars"] = $true