Teamcity - generate artifact from Powershell - powershell

I'm using a Powershell build step and want to generate a file and have it included in the artifacts. Here's what I tried, but it doesn't appear:
param(
[parameter(Mandatory=$true)] [string]$controller
)
Write-Output "Controller: $controller"
$testsettingsXML = #"
<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="Remote" id="36b029f0-1e34-4c17-b7b1-3e6a0284a08e" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>These are default test settings for a local test run.</Description>
<RemoteController name="$controller" />
<Execution location="Remote">
<TestTypeSpecific>
<UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
<AssemblyResolution>
<TestDirectory useLoadContext="true" />
</AssemblyResolution>
</UnitTestRunConfig>
</TestTypeSpecific>
<AgentRule name="AllAgentsDefaultRole">
</AgentRule>
</Execution>
<Properties />
</TestSettings>
"#
#write the testsettings file out to disk.
$testsettingsXML | Out-File -FilePath "./remotehack.testsettings" -Encoding utf8
I did a similar thing in a metarunner and it worked just fine. Why not here?

The issues were three-fold.
Last line of the Powershell should have looked like this:
$testsettingsXML | Out-File -FilePath "remotehack.testsettings" -Encoding utf8
I didn't include remotehack.testsettings/remotehack.testsettings as an Artifact Dependency
I didn't include the Artifact Path "remotehack.testsettings" under General Settings.
A few newbie mistakes.

Related

Powershell Command Wont Read XML

I have a powershell script I'll be using to enhance a publish to folder profile. I want to be able to read my appsettings.json 'versionNumber' property and move the resulting file around and create a nice zip. I realize I can do all this with MSBUILD directly, but this is for a .net core project and im concerned about not doing it correctly. So for now I'm using a PS script AND a publish profile.
The powershell script refuses to read in the publish profile xml. Here is the PS script:
$foundPublishProfile = Test-Path '..\Properties\PublishProfiles\FolderProfile.pubxml'
if (!$foundPublishProfile)
{
Write-Host 'ERROR: Expected to find "..\Properties\PublishProfiles\FolderProfile.pubxml"' -ForegroundColor DarkRed
Write-Host ''
exit
}
[xml] $publishProfileXML = Get-Content '..\Properties\PublishProfiles\FolderProfile.pubxml'
Write-Host $publishProfileXML
$publishFilePath = $publishProfileXML.SelectSingleNode('//Project/PropertyGroup/PublishUrl') #| Select-Object -Expand '#text'
#$publishFilePath = Select-Xml -XPath '//Project/PropertyGroup/PublishUrl' -Path '..\Properties\PublishProfiles\FolderProfile.pubxml'
Write-Host $publishFilePath
And here is the publish profile, pubxml file:
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeleteExistingFiles>False</DeleteExistingFiles>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>F:\Fileshare\NCHIWebSite</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>f7da8cd0-d62c-47c5-9ae7-7c9cb9a6d23d</ProjectGuid>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>
I expected to see something for the output, whether its from the loaded XML content or from my XPATH select single node, but instead I'm getting nothing. I've tried many variations. Is there something I'm doing incorrectly?
Your input XML uses namespaces, which you must account for when you use XPath queries with .SelectSingleNode() - elements that are part of a namespace cannot be located by their mere element name (such as Project), even if they're only implicitly part of a namespace via an ancestral xmlns attribute.
However, it is often simpler to use PowerShell's convenient, property-based adaptation of the XML DOM, which allows you to use simple dot notation to drill down to the element(s) of interest, and which ignores namespaces:
$publishProfileXM.Project.PropertyGroup.PublishUrl
See this answer for additional information, including how to alternatively use the Select-Xml cmdlet with XPath queries and namespaces.

Powershell Invoke-WebRequest causing server error

I have an xml file and I'm using a Powershell script to create a soap request to post it to the url.
The problem I seem to have is with this line of code below producing a server error:
[xml]$res = Invoke-WebRequest $uri -Method POST -ContentType "text/soap+xml;charset=windows-1252" -Body $soapReq
The odd thing is when I open my data XML file in notepad and save it (make no changes), the Powershell script seems to work and transfer the file across to the target url.
I'm writing my xml file in UTF8 charset.
I enclose a tweaked version of Powershell script I am using.
#my db generates this file
$filePath = "C:\MyPath\data.xml"
$result = Get-Content $filePath
#rewrite the file to a specific location and encode it to utf-8
Out-File -FilePath $filePath -InputObject $result -Encoding 'UTF8'
$un = 'un'
$Pw = 'pw'
$xmlData = Get-Content $filePath -Raw
$xmlData = [Security.SecurityElement]::Escape($xmlData)
$soapReq = #"
<?xml version="1.0" encoding="Windows-1252"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="https://url">
<env:Body>
<ins0:Transfer>
<sourceIdentifier>123456</sourceIdentifier>
<userName>$un</userName>
<password>$pw</password>
<xmlDataset><?xml version="1.0" encoding="UTF-8"?>
$xmlData</xmlDataset>
</ins0:Transfer>
</env:Body>
</env:Envelope>
"#
$uri = "https://url"
[xml]$res = Invoke-WebRequest $uri -Method POST -ContentType "text/soap+xml;charset=windows-1252" -Body $soapReq
Before I investigate using vbs to open the file in notepad and save it (overkill), I’m hoping someone can give me advice on how to get this Powershell script working properly.

TFS 2013 - Update MSBuild Parameters from pre-build PowerShell script

I have recently upgraded from TFS 2012 to TFS 2013 and am trying to use the new template (TfvcTemplate.12.xaml). I need to set the version numbers of my .NET applications and WiX installers as part of this process.
In my TFS 2012 process I customised the build template using TFS Community Build Extensions to do the following:
Generate a version number. The Major and Minor version numbers are static, the release number is the number of days since 2014-11-25, the build number is the number of times the build definition has run today.
Update all AssemblyInfo.cs files with the new version number
Update the MsBuildArguments argument to pass the version as a parameter. This is so that I can set the version number in my WiX installers.
Before I resort to customising the build template again I would like to try to achieve the above using a Pre-build PowerShell script.
Items 1 and 2 were easy in PowerShell but I am stuck with the third requirement. Is it possible for the Pre-build PowerShell script to update the MSBuildArguments?
I think there is a simpler way to do this. This is what we did.
Created an include file called Version.wsi. The file contains these 4 lines:
<?xml version="1.0" encoding="utf-8"?>
<Include>
<?define CurrVersion="1.0.0.0" ?>
</Include>
In our wxs file, right after the line
<Wix xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" xmlns="http://schemas.microsoft.com/wix/2006/wi" >
add the following:
<?include Version.wxi ?>
In your wxs file use the variable $(var.CurrVersion) where you currently specify your version.
Now all you need to include in your powershell script is some code to modify the wxi file with the correct version number. This is how I do it:
function UpdateVersion($newVersion)
{
$wxiFile = gci "$Somedir\Version.wxi"
#clear the read-only bit just in case
Set-ItemProperty $wxiFile -name IsReadOnly -value $false -Force
$newContent = $content -replace "1.0.0", $prodVersion
Set-Content -Path $wxiFile -Value $newContent
}

Can MSBUILD receive NEW 'user' level environment variables without restarting Visual Studio?

I have a NuGet package that sets up some PowerShell cmdlets in its Init.ps1 file, and one of the things I'd like them to be able to do is set environment variables that are passed to a build in Visual Studio.
In my Init.ps1 script I use the line:
[Environment]::SetEnvironmentVariable("MyVariable", $someValue, "User")
...to set a 'User' level environment variable, figuring that a regular 'Process' level variable won't work since Package Manager Console is in a different process than MSBuild. Also, manually setting $env:MyVariable = "foo" in Package Manager Console does not pass its value to MSBuild.
In MSBuild, a regular $(MyVariable) is not populated with 'foo' as desired.
If I use [Environment]::GetEnvironmentVariable('MyVariable'), the overload that normally lets me target EnvironmentVariableTarget.User is not available.
The goal is to be able to drop to Package Manager Console, run an arbitrary cmdlet and have the changes persisted in properties during build. Answers that require reboot, restart or reloading a solution aren't what I'm looking for.
Am I missing something about environment variables?
Is there another simple way to set build properties from Package Manager Console that I've overlooked (short of using EnvDTE or Microsoft.Build to manually edit each project's csproj file?
Update - some further discoveries:
The environment variables are set correctly, and I can echo them back from command prompt too.
If I restart Visual Studio completely then the variable finally reaches MSBUILD, but then subsequent changes to the variable aren't picked up.
Seems like Visual Studio is caching the environment variable. Is there a way to 'refresh' a process' environment variables?
If I use [Environment]::GetEnvironmentVariable('MyVariable'), the overload that normally lets me target EnvironmentVariableTarget.User is not available.
Are you sure?
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo">
<Exec Command="setx MyVariable Foo" />
<Exec Command="echo 1. %MyVariable%" />
<Exec Command="echo 2. $(MyVariable)" />
<PropertyGroup>
<MyVariable>$([System.Environment]::GetEnvironmentVariable('MyVariable', System.EnvironmentVariableTarget.User))</MyVariable>
</PropertyGroup>
<Message Text="3. $(MyVariable)" />
</Target>
</Project>
At the end of the day, no, I couldn't find a way to get the Environment variables without a restart.
In the end I solved this by using a separate Properties.targets (arbitrary name) to store my 'variables' like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Storage for global configuration properties -->
<PropertyGroup>
<MyVariable1></MyVariable1>
<MyVariable2></MyVariable2>
</PropertyGroup>
</Project>
and importing that into my build script with the following line:
<Import Project="Properties.targets" />
Then, to manipulate the variables I use two powershell functions, passing it $toolsPath from Init.ps1, and using this to set properties:
function SetPackageProperty($toolsPath, $name, $value) {
$propertiesFile = [System.IO.Path]::Combine($toolsPath, "Properties.targets")
$msbuild = New-Object -TypeName Microsoft.Build.Evaluation.Project -ArgumentList $propertiesFile
$var = $msbuild.Xml.AddProperty($name, $value)
$msbuild.Save()
[Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.UnloadProject($msbuild)
}
And to get properties:
function GetPackageProperty($toolsPath, $name) {
$msbuild = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1
$propertiesFile = [System.IO.Path]::Combine($toolsPath, "Properties.targets")
$msbuild = New-Object -TypeName Microsoft.Build.Evaluation.Project -ArgumentList $propertiesFile
$var = $msbuild.Xml.Properties | Where-Object {$_.Name -eq $name} | Select-Object -First 1
[Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.UnloadProject($msbuild)
return $var.Value
}
You can import Properties.targets into any build script you want to use the properties.
Hope this helps somebody!

Add-Type load assembly from network UNC share error 0x80131515

When you want to add an assembly from a network UNC share using the command:
$scriptPath = Split-Path ($MyInvocation.MyCommand.Path)
Add-Type -path "$scriptPath\selenium-dotnet\net40\WebDriver.dll"
you might face such an error:
Add-Type: Could not load file or assembly 'file:///Z:\A-Backup\Users\Administr
ator\Desktop\MAXIMO Automatic\selenium-dotnet\net40\WebDriver.dll' or one of it
s dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515
)
At Z:\A-Backup\Users\Administrator\Desktop\MAXIMO Automatic\MAXIMO Automatic.ps
1:14 char:1
+ Add-Type -path "$scriptPath\selenium-dotnet\net40\WebDriver.dll"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Add-Type], FileLoadException
+ FullyQualifiedErrorId : System.IO.FileLoadException,Microsoft.PowerShell
.Commands.AddTypeCommand
How can I fix this problem?
The key is to allow for loading an assembly from a network path for a PowerShell executable. It can be done by creating the two files
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe.config C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe.config
and paste this code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>
Re: ALIENQuake's. I've added your fix to a PS script to install the files in the correct locations.
$PSPaths = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe.config','C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe.config'
$XMLCode = #"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>
"#
foreach($PSConfigFile in $PSPaths) {
$xmlcode | Out-File -FilePath $PSConfigFile -Encoding utf8
}
Instead of Add-Type you could:
[System.Reflection.Assembly]::UnsafeLoadFrom('\\uncpath\driver.dll')
It'll ignore some security settings and load the library but it's, well, unsafe ;).