PowerShell Script in PostBuild - powershell

Continuous Integration
I have been working on a PowerShell script to keep our development process streamlined. I was planning on running it as a post-build event, but I'm having some trouble.
From the PowerShell prompt, the following works wonderfully:
PS C:\> ./example.ps1
However, when attempting to run this from cmd.exe as follows:
C:\> powershell -command "&\"C:\path to script\example.ps1\""
The script executes but I get a round of errors back from PowerShell, consisting mostly of path resolution errors from the resolve-path function:
Resolve-Path : Cannot find path 'C:\Documents and Settings\bdunbar\My Documents
\Visual Studio 2008\Projects\CgmFamilyComm\FamilyComm\iirf\cms\isapirewrite4.dl
l' because it does not exist.
At C:\Documents and Settings\bdunbar\My Documents\Visual Studio 2008\Projects\C
gmFamilyComm\scripts\cms.ps1:4 char:27
+ $iirfpath = (resolve-path <<<< ../iirf/cms/isapirewrite4.dll).path,
Resolve-Path : Cannot find path 'C:\Documents and Settings\bdunbar\My Documents
\Visual Studio 2008\Projects\CgmFamilyComm\FamilyComm\familycomm' because it do
es not exist.
At C:\Documents and Settings\bdunbar\My Documents\Visual Studio 2008\Projects\C
gmFamilyComm\scripts\cms.ps1:5 char:27
+ $vdirpath = (resolve-path <<<< ../familycomm).path
Is there a way to work around this? Could it be an issue with running resolve-path under cmd.exe?
[Update]
I've been able to change things to get around the errors that are occurring, but I still receive errors that work perfectly fine from the powershell command prompt. I can't figure out what the difference is.

I've made this work in the past (see http://sharepointpdficon.codeplex.com/SourceControl/changeset/view/13092#300544 if interested):
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe -NoLogo
-NonInteractive -Command .'$(ProjectDir)Deployment\PostBuildScript.ps1'
-ProjectDir:'$(ProjectDir)' -ConfigurationName:'$(ConfigurationName)' -TargetDir:'$(TargetDir)' -TargetFileName:'$(TargetFileName)' -TargetName:'$(TargetName)
Then throw these parameters in the first line of your post-build script (if you think you may be able to use them):
param($ProjectDir, $ConfigurationName, $TargetDir, $TargetFileName)
Also I should point out, I am not using this presently. I did like using it as a quick scratchpad to reload test data for running integration tests.

Looks like your problem is how relative paths are resolved. Relative paths are resolved based on the current location (stored in $pwd) and not based on the location of the script. So if you launched the script from C:\, it definitely would not work.
I would suggest you calculate the paths based on an argument (like Peter Seale shows), or grab the actual location of the script from:
$MyInvocation.MyCommand.Path

Related

Newbie powershell argument issues

I made powershell script that [1] accepts 2 arguments (aka parameters), [2] changes a file's modified date & time, and [3] writes something to host. The following command line works just fine in the powershell console, but triggers an error message when I run the same command line in a Windows cmd prompt (DOS) Window:
E:\Apps\UtilitiesByMarc\Change_DateTime_for_test1.bat_and_Hello_world_with_2_named_args_aaa.ps1 -dateTimeVarArg "01/11/2005 06:01:36" -file_dateTimeMod_fullname "E:\Apps\Delete01\test1.bat"
The following is the coding for the powershell script to which I gave the long name, 'Change_DateTime_for_test1.bat_and_Hello_world_with_2_named_args_aaa.ps1':
param ( [string]$dateTimeVarArg, [string]$file_dateTimeMod_fullname)
Get-ChildItem $file_dateTimeMod_fullname | % {$_.LastWriteTime = $dateTimeVarArg}
#Get-ChildItem "E:\Apps\Delete01\test1.bat" | % {$_.LastWriteTime = $dateTimeVarArg}
$strString = "Hello World"
write-host $strString
function ftest{
$test = "Test"
write-host $test
}
ftest
When I run the command line shown above in a Windows DOS command prompt setting, I get the following error message:
Exception setting "LastWriteTime": "Cannot convert null to type "System.DateTime"."
At E:\Apps\UtilitiesByMarc\Change_DateTime_for_test1.bat_and_Hello_world_with_1_named_arg_aaa.ps1:6 char:50
+ ... "E:\Apps\Delete01\test1.bat" | % {$_.LastWriteTime = $dateTimeVarArg}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
I would like to know [1] how to alter the command line shown above (which works fine in the powershell console) so that it works in a Windows DOS command prompt setting, and [2] where I can learn more about why my command line triggers errors, and how to avoid them.
According to the output from the command "Get-Host | Select-Object Version", I am running version. 5.1.19041.1682.
Any tips would be much appreciated.
By default, you can not directly execute PowerShell scripts (.ps1 files) from cmd.exe, the Windows legacy shell, or from outside PowerShell altogether.
Attempting to do so opens the script file for editing instead, as does double-clicking .ps1 files from File Explorer / the desktop.
Executing .ps1 scripts from outside PowerShell itself requires use of the PowerShell CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+), which in your case translates to a call such as (additional CLI parameters may be called for):
powershell.exe -File E:\Apps\UtilitiesByMarc\Change_DateTime_for_test1.bat_and_Hello_world_with_2_named_args_aaa.ps1 -dateTimeVarArg "01/11/2005 06:01:36" -file_dateTimeMod_fullname "E:\Apps\Delete01\test1.bat"
As for what you tried:
The fact that you were able to execute your .ps1 from cmd.exe suggests that you changed the file-type definition for such files to execute via powershell.exe instead.
The fact that the arguments you tried to pass to the script were ignored - as implied by the error message you got - suggests that you used File Explorer's Open with shortcut-menu command to choose to open all .ps1 files with powershell.exe; said method does not support argument-passing.
There is a way to change the file-type definition to support argument-passing too, and it is detailed in this answer (section "Programmatic method").
Generally, however, I suggest not applying this customization, especially in batch files that must also be run by other users / on other machines, which cannot be expected to have the same customization in place.

Custom VSTS Build Task to Call PhantomJS Fails to Create Image File

I am in the process of writing a custom VSTS extension that (based on parameters) requests a URL and emails the results. I have all pieces of it functioning properly, except the piece that calls PhantomJS exe. This is required in order to generate the screenshot, prior to attaching to the email.
The relevant PowerShell code is as follows:
Write-Verbose "Run phantomjs with $testingurl"
Start-Process -FilePath "phantomjs.exe" -ArgumentList "screenshot.js",$testingurl
$directory = Get-ScriptDirectory
Get-ChildItem $directory -force
I am calling PhantomJS with the relevant parameters, which should create an image in my working directory called 'example.png'. However, when I then output the contents of the directory, the file is not present.
I have played around with the "scope" of my extension, but it has not helped. My current scopes are:
"scopes": [
"vso.build_execute",
"vso.serviceendpoint_manage",
"vso.code_manage",
"vso.packaging_manage",
"vso.release_execute",
"vso.work_write"
],
I also verified that I can create a file if I use the standard PS commandlet:
New-Item "$directory\test.txt" -ItemType file
I also tried including a blank 'example.png' in my VSIX package that would hopefully be overwritten, but it does not.
So, I'm convinced it's not a write-permission issue. The VSTS logs give no indication of the call to PhantomJS exe failing.
Are you not allowed to call an exe from a PS script from within a VSTS extension (build task)? The PS script works correctly locally so I feel this is more of an environment/permission/allowance issue than a code issue on my part.
My issue ended up being that the exe was run and my code continued to execute. I had to add the "-Wait" attribute to my call:
Start-Process -FilePath "phantomjs.exe" -ArgumentList "screenshot.js",$testingurl -Wait
Since the lines following the call to phantomjs.exe requires the execution to finish, the "-Wait" ensures it runs to completion first.

Module returns different result from the script version [Test-Path]

introduction
I've written my first PowerShell script aimed for retrieving detailed information from a Windows Setup ISO file. Once the basic features achieved, I've started to convert the ps1 script into a psm1 module. I hoped the result would be the module just work like the script but I'm facing issue I'm not able to solve.
You can download my work here, script version and module (roughly translated to English from French).
I successfully installed the module in PSModulePath in:
[Environment]::GetFolderPath("mydocuments")\WindowsPowerShell\Modules
Command usage is very simple. You call it like that:
WinIsoInfo [[-Path] <String>] [<CommonParameters>]
Help is provided by module: man WinIsoInfo
Usage Example:
WinIsoInfo -Path "E:\Win 10\Installation\ISO\Windows 10 x64 fr.iso"
The ps1 script version is the exact same code as the psm1 module but there are commands examples at the end of the file that you can un-comment and edit before running the script.
Current Status
All the tests are and need to be run as admin, in console or PowerShell ISE.
The ps1 script works as expected but the psm1 module doesn't produce the same result.
At line 108 of the code, there is a Test-Path in a Switch statement:
{(Test-Path "$wimPath\sources\install.wim") -or (Test-Path "$wimPath\sources\install.esd")}
In the ps1 script, this Test-Path return True and user get the expected info.
But in the psm1, it seems to return False since Switch statement jump to the next test after this one. So at the end the user gets that the ISO doesn't contain windows setup. I can assure that the Test-Path should return True because I manually checked it while the function was paused by breakpoints.
Hint and lead
There are 2 cases where I manage to get the module work as expected. But only using in PowerShell ISE, NOT in console.
Using Automatic Variable $? in console pane while debugging module
Step to reproduce:
PowerShell ISE is not running.
Open PowerShell ISE as admin.
In console pane, run import-module Get-WinIsoInfo -Force -Global -Verbose or import-module -path X:\Path\To\Modules\Get-WinIsoInfo -Force -Global -Verbose
In console pane, run WinIsoInfo -Path "X:\path\to\AnyWindowsSetup.iso"
In my case, at this point, the command returns there is no Windows Setup in ISO file.
Now open the Get-WinIsoInfo.psm1 and put a breakpoints anywhere between line 90-108.
do step 4 again
While the script is paused at breakpoints, run $? in the console pane then press F10 then F5
And "voilĂ  !" the module return the expected result and will keep working but only during PowerShell ISE session and inside PowerShell ISE. Command run in console still won't work. And the next time I run PowerShell ISE, the module won't find the setup image path again.
Previously run the ps1 script version in PowerShell ISE
Step to reproduce:
PowerShell ISE is not running.
Open PowerShell ISE as admin.
In console pane, run import-module Get-WinIsoInfo -Force -Global -Verbose or import-module -path X:\Path\To\Modules\Get-WinIsoInfo -Force -Global -Verbose
In console pane, run WinIsoInfo -Path "X:\path\to\AnyWindowsSetup.iso"
In my case, at this point, the command returns there is no Windows Setup in ISO file.
Now open the Get-WinIsoInfo.ps1 script, edit a valid command at the end of the code then press F5 to run it.
Note: Since the command in script has the same name as the module previously imported, at this point I don't know if the triggered function is the one from the ps1 script or the one from the module. Tell me if you know.
The script returns the expected result as Windows Setup info.
Close the ps1 file (it is no longer needed in PowerShell ISE for the next to work)
do step 4 again
And "voilĂ  !" the module return the expected result and will keep working but only during PowerShell ISE session and inside PowerShell ISE. Command run in console still won't work. And the next time I run PowerShell ISE, the module won't find the setup image path again.
Conclusion
After the Hint and lead tests, I found out that they were some differences from modules imported in session before and after success. These key modules loaded by PowerShell ISE are Storage and Microsoft.WSMan.Management. I thought I found the solution and added this line to manifest:
RequiredModules = #("Storage";"Microsoft.PowerShell.Management";"Microsoft.PowerShell.Security";"Microsoft.PowerShell.Utility";"Microsoft.WSMan.Management")
I added all the modules that was present after the module works as expected, just to be sure.
I did the same for assemblies but 2 of them could not be imported: Microsoft.Management.Infrastructure.UserFilteredExceptionHandling and Microsoft.Management.Infrastructure.resources
Resulting in this new manifest line:
RequiredAssemblies = #("Microsoft.Management.Infrastructure.Native";"Microsoft.WSMan.Runtime";"System.Security")
Unfortunately, it seems it is not enough to solve the issue.
Maybe other things has to be imported or it's a wrong lead.
I really hope you could reproduce the bug or at least I hope the Hint and lead section will lead you to find the cause and a solution. I'm too novice to understand why this happens on my system.
My setup uses PowerShell v5.0 with Win 8.1 Pro.

wusa silent install error

I am trying to automate updating Powershell on Windows 7 using Windows6.1-KB2506143-x64.msu, and having a heck of a time. The following code works fine in a standalone ps1 file. And it works in my main ps1 file. But when run from a module it fails with exit code -2145124341. This is in PS v2, where negative exit codes are handled wrong, so that number is perhaps useless, and FWIW I have a good 40 other installers of various types that work from this module. However, this is my first attempt at automating msu files, so maybe there is a known interaction here that I haven't discovered yet? There's thousands of lines of code between the root ps1 file where this works and the module where it doesn't, so tracking down what is triggering the error is going to be a beast without some sort of trail to follow at the very least. So, anyone have an idea where I should start?
$filePath = 'wusa.exe'
$argumentList = '"\\PX_SERVER\Rollouts\Microsoft\Windows6.1-KB2506143-x64.msu" /quiet /norestart'
$exitCode = (Start-Process -filePath:$filePath -argumentList:$argumentList -wait -errorAction:stop -passThru).exitCode
Also, running wusa.exe leaves some detritus in the script folder, but only when it is run from the module. Is this an issue with the msu file, or just a bug in wusa? Or does it point at what is causing the issue perhaps?
I had hoped to get this update to work to enable some new features, but between not being able to automate and garbage being left behind, I am very close to abandoning that path and juts continuing to target v2. But hopefully someone can point me in the right direction as that is not my preferred solution at all.
a few toughts on first reading :
The ArgumentList parameter for Start-process needs an ARRAY to work well :
$argumentList = #( "\\PX_SERVER\Rollouts\Microsoft\Windows6.1-KB2506143-x64.msu", "/quiet", "/norestart" )
wusa.exe takes a log parameter : /log:c:\fso\install.log can you had it to your script for this particular package to check what happens ?
a powershell script trying to update powershell ... I'm not quite sure this is meant to work ... it's the only case in wich i'll backup on another scrpting language (people, please correct me if i'm wrong ... )
Please let me know the result of the wusa.exe /log command, thanks

$MyInvocation.MyCommand.Path returning NULL

I have the following code as the beginning of a longer script:
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path
& $ScriptPath\build_functions.ps1
& $ScriptPath\build_builddefs.ps1
The idea is to get the path of the script being run and use that path to call some supporting scripts. However when I went to test this out in isolation to make sure it could work (by highlighting that block and running just that code), I got the following error:
Split-Path: Cannot bind argument to parameter 'Path' because it is null.
Interestingly enough, when I run the entire script it seems to run these files separately. Is there something I'm missing about how the ISE handles running a selection rather than the full script? Does it not establish a file system context when you run a selection?
$MyInvocation is an automatic variable populated at script run time, then if you execute $MyInvocation.MyCommand.Path in a powershell console or ISE isn't populated;
that's why in your test the $ScriptPath has no value ($null)
I don't if what happened to me was why some where seeking null in $MyInvocation.MyCommand.Path, but I will explain how I found the solution.
I had scripts that were working in production, yet when I loaded the .ps1 file up and tried to get $MyInvocation.MyCommand.Path it was null. My version of Powershell was 4.0, but 1.0 for the ISE (%windir%\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe).
But it didn't occur to me at first why they should work, yet when I manually inspected $MyInvocation.MyCommand.Path or $MyInvocation in PowerShell why it was null, and why I was getting a null error for Split-Path -parent $MyInvocation.MyCommand.Path.
So I thought I needed to upgrade the powershell to 5.1 on my Windows 2012 R2 Server like on my desktop NUC PC.
The real problem was that I found that if I set a break point in my .ps1 file and ran it to the spot where I was doing:
$ScriptDir = Split-Path -parent $MyInvocation.MyCommand.Path
That it worked. Of course it worked, I had been using it for a while. Why didn't I see it before?
What went wrong? I was trying to manually run the PowerShell in pieces using the run step command, when I had never run the script before since PowerShell was open!
I have to say this is probably a No Duh moment.
But we had recently had a server crash and had it restored (VSphere Clustered) and reseeded, so I thought maybe I have an older version of PowerShell.
PowerShell allows you to have multiple files/windows open inside it, yet the variables are shared between them. Apparently until you actually try to run the script (not step by step run), it has no script file executing and can't get you the path.
I hope this post saves someone from wasting a bunch of time like I did!
i think you need write just:
& .\MySuperDuperScript.ps