How to set a value for MSBuild property using PowerShell - powershell

I have a property to specify the build drive:
<PropertyGroup>
<BuildDrive Condition="'$(BuildDrive)'==''">Y:</Group>
</PropertyGroup>
If I want to change the build drive using a batch file, I can do as follows:
#echo off
set buildDrive=H:
:: Then call MSBuild
Msbuild /t:BuildTarget %Projectfile% %Logger%
Now I want achieve the same using PowerShell.
I tried as follows in my PowerShell script, build.ps1:
$BuildDrive=H:
MSbuild /t:BuildTarget $ProjectFile $Logger
But it is not honoring the drive letter provided through $BuildDrive.
I knew I could achieve it if I passed a parameter as follows, but when the number of properties are more, this approach was not handy.
$BuildDrive=H:
Msbuild /t:BuildTarget /p:BuildDrive=$BuildDrive $projectfile $logger
How do I pass a PropertyGroup value through PowerShell?

You are setting environment variables. These are available as properties in MSBuild.
You can do the following in PowerShell:
$env:BuildDrive="H:"

Related

Powershell Pre-Build script failing from msbuild

I have a Powershell script that executes as a pre-build call for a Xamarin mobile app. The script changes the package name to match the build type e.g. Debug, Release.
To enable different "flavours" of the app to be created, I have written a batch file to replace the config file of the app with one matching the requested build type.
When I build from within Visual Studio, the powershell script runs as I expect and changes what I expected. However when the batch file runs I get an error message appearing:
Here is the content of the batch file, this was my first attempt at writing a batch file to build a code project:
#ECHO OFF
set buildVer=%1
set path=XamarinTestApp\XamarinTestApp
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v4.0.30319
echo %buildVer%
IF "%buildVer%"=="Release" (
goto :releaseBuild)
IF "%buildVer%"=="Test" (
goto :testBuild)
IF "%buildVer%"=="Dev" (
goto :devBuild)
:releaseBuild
set buildType=Release
copy /-y %path%\app.Release.config %path%\app.Config
goto :builder
:testBuild
set buildType=Release
copy /-y %path%\app.Test.config %path%\app.Config
goto :builder
:devBuild
set buildType=Debug
copy /-y %path%\app.Debug.config %path%\app.Config
goto :builder
:builder
call %msBuildDir%\msbuild.exe
C:\Projects\%path%\XamarinTestApp.Droid\XamarinTestApp.Droid.csproj /property:Configuration=%buildType% /target:SignAndroidPackage /verbosity:diag
I'm looking for any advice on either the error message I am getting, or some advice on how to get configurable information into my app.
Thanks.
Do not use path as user-variable. It is predefined by the system to locate executables.
Change that name to mypath - almost anything other than path.
from the prompt, use the command
set
to see a partial list of the names of variables that are set by the system

PowerShell Syntax Error

I am working on a powershell script that will create TFS build definitions. I have used below example as my starting point.
http://geekswithblogs.net/jakob/archive/2010/04/26/creating-a-build-definition-using-the-tfs-2010-api.aspx
I have the script done in powershell and it creates me a build definition file in TFS. One thing I am stuck in is creating Process information such as "Item to build" and "Projects to build". The C# code for this is given below
//Set process parameters
varprocess = WorkflowHelpers.DeserializeProcessParameters(buildDefinition.ProcessParameters);
//Set BuildSettings properties
BuildSettings settings = newBuildSettings();
settings.ProjectsToBuild = newStringList("$/pathToProject/project.sln");
settings.PlatformConfigurations = newPlatformConfigurationList();
settings.PlatformConfigurations.Add(newPlatformConfiguration("Any CPU", "Debug"));
process.Add("BuildSettings", settings);
buildDefinition.ProcessParameters = WorkflowHelpers.SerializeProcessParameters(process);
Below is the powershell code I have written to achive above.
Write-Host"Set process parameters "$now
$process=[Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::DeserializeProcessParameters($def.ProcessParameters)
Write-Host"Set build settings properties "$now
$settings=new-object-`enter code here`TypeNameMicrosoft.TeamFoundation.Build.Workflow.Activities.BuildSettings
$sList=New-Object-TypeNameMicrosoft.TeamFoundation.Build.Workflow.Activities.StringList
$sList="$/pathToProject/project.sln"
$settings.ProjectsToBuild =$sList
$process.Add("BuildSettings", $sList)
But the above segment of code does not create me the Build settings in my build definition file. Myquestion is am I doing this the correct way in powershell? I feel I am not writing the powershell code incorrectly as I am newbie to powershell. Any guidance and
help would be appreciated
Calling a constructor with parameters should be done like this in PowerShell:
$ns = 'Microsoft.TeamFoundation.Build.Workflow.Activities'
$settings.ProjectsToBuild = new-object "$ns.StringList" '$/pathToProject/project.sln'
Also note the use of single quotes around the TF server path. $ is s special character in PowerShell - tells it what follows is either a variable name or sub-expression even in a string. Unless that string is single quoted. In which case, PowerShell doesn't interpret any special characters within the string.

TFSBuild to include an array in parameters to powershell

I have a TFS 2010 build which calls a powershell script for deployment. I've defined several arguments for the build script and these have worked great. They are used by the build and also included in the arguments that are passed into Powershell via the Arguments property of the InvokeProcess control.
I now have a requirement for the powershell script to deploy to a variable number of servers, so I'd like to pass the server ID's in on the argument list from TFS.
In the build definition, I have declared a new argument called TargetServers of type string[]. I have populated this from the Build Process Parameters dialog prior to executing a build.
I have set the FileName property of the InvokeProcess control to "Powershell", and the Arguments property as follows:
String.Format(" ""& '{0}' '{1}' '{2}' '{3}' '{4}' '{5}' '{6}' '{7}' '{8}' '{9}' "" ", DeploymentScriptFileName, IO.Path.GetDirectoryName(DeploymentScriptFileName), "ExecuteBizTalkAppMSI.ps1", MSIFileName, BTDFFilename, TargetServerPath, TargetServers, ServerDeploymentFolder, InstallFolder, HostInstanceFilter, ApplicationName)
My problem is that the TargetServers argument being passed to Powershell is simply System.String[].
From the build log I can see the following output of the Invoke Process control:
Powershell "& 'C:\Builds\3\x.Int.MIS.Deployment\CopyDeployScriptThenExecute.ps1'
'C:\Builds\3\\x.Int.MIS.Deployment' 'ExecuteBizTalkAppMSI.ps1'
'x.Int.MIS-3.0.0.msi' 'x.Int.MIS.Deployment.btdfproj'
'\\d-vasbiz01\BizTalkDeployment' 'System.String[]' 'c:\BizTalkDeployment'
'c:\Program Files (x86)\x.Int.MIS for BizTalk 2010\3.0' 'BTSSvc*MIS*' "
Can anyone please advise how to pass the array?
As strings delimited by commas:
PS> function foo([string[]]$x){$x}
PS> foo a,2,3
a
2
3
If you want, you can put quotes around each individual item but you don't need to unless they contain spaces or other characters reserved for the syntax.

MSDeploy runCommand using relative path

I am trying to run a command as a part of my packaging/deployment process via MSDeploy. In particular, I am trying to create a custom event log by running installutil against one of my DLLs, but I am having trouble with specifying a relative path to the DLL from the deployment directory. To get started, I added the below config to my csproj in order to generate the runCommand provider inside of my Manifest file. Please note the absolute path to the DLL.
<PropertyGroup>
<!-- Extends the AfterAddIisSettingAndFileContentsToSourceManifest action to create Custom Event Log -->
<IncludeEventLogCreation>TRUE</IncludeEventLogCreation>
<AfterAddIisSettingAndFileContentsToSourceManifest Condition="'$(AfterAddIisSettingAndFileContentsToSourceManifest)'==''">
$(AfterAddIisSettingAndFileContentsToSourceManifest);
CreateEventLog;
</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<Target Name="CreateEventLog" Condition="'$(IncludeEventLogCreation)'=='TRUE'">
<Message Text="Creating Event Log" />
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>installutil C:\inetpub\wwwroot\MyTestApp\bin\BusinessLayer.dll</path>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<ItemGroup>
After calling msbuild, this generated my manifest correctly inside of my package.zip. When I ran MyTestApp.deploy.cmd /Y it correctly called msdeploy and deployed my files to inetpub\wwwroot\MyTestApp and ran my command from the manifest below:
<runCommand path="installutil C:\inetpub\wwwroot\MyTestApp\bin\BusinessLayer.dll ... etc
The problem I am having is I do not want to hardcode this DLL path to c:\inetpub\etc. How can I make the above call using the relative path from my deployment directory under Default Web Site? Ideally, I would like MSDeploy to take this path and pass it as a variable to the runCommand statement in order to find the DLL. Then I could write something like: <path>installutil $DeploymentDir\NewTestApp\bin\BusinessLayer.dll</path> without having to worry about hard-coding an absolute path in.
Is there any way to do this without using the absolute path to my DLL every time?
You can add definition of DeploymentDir to the .csproj with the action you wrote above:
<PropertyGroup>
<DeploymentDir Condition="'$(Configuration)'=='Release' AND '$(DeploymentDir)'==''">Release Deployment Dir</DeploymentDir>
<DeploymentDir Condition="'$(Configuration)'=='Debug' AND '$(DeploymentDir)'==''">Debug Deployment Dir</DeploymentDir>
<DeploymentDir Condition="'$(DeploymentDir)'==''">C:\inetpub\wwwroot</DeploymentDir>
<AplicationName Condition="'$(Configuration)'=='Release' AND '$(AplicationName)'==''">NewTestApp</AplicationName>
<AplicationName Condition="'$(Configuration)'=='Debug' AND '$(AplicationName)'==''">MyTestApp</AplicationName>
<ApplicationDeploymentDir Condition="'$(ApplicationDeploymentDir)'==''">$(DeploymentDir)\$(ApplicationName)\bin</ApplicationDeploymentDir>
</PropertyGroup>
Theese conditions will allow to change everything from command line to take full control over the build process in your build system or script.
MSBuild.exe yourproj.proj /p:Configuration=Release /p:DeploymentDir=D:\package /p:ApplivationName=BestAppForever
And inside of your task you can use it
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>installutil $(ApplicationDeploymentDir)\BusinessLayer.dll</path>
</MsDeploySourceManifest>
</ItemGroup>
I realize this isn't the answer you probably wanted to hear but this is how I got around it.
We created a powershell script on the destination server. So instead of running your command:
installutil C:\inetpub\wwwroot\MyTestApp\bin\BusinessLayer.dll ... etc
We would run:
c:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe d:\powershell\installSites.ps1 siteName <NUL
The "sitename" is being passed in as a param into the powershell script. Inside the script it knows on that destination server which files to install, any commands that need to run, app pools to recycle, etc.
Again, not as easy as finding a relative path, but it does the job.

Reading/Capturing DOS input for use in MsBuild

How do I capture/read DOS input for use in MsBuild?
EDITED for clarification
Currently I have 2 files. One batch file, the other is the core.msbuild file which contains the msbuild stuff. I want to be able to capture an extra user input e.g. an output directory, from the windows command prompt (when the build file is executed) and send it to the msbuild file (and set it to a PropertyGroup). %1 is already taken so I'm thinking to use %2.
Like the following:
build.bat param1 param2
param2 is the one im trying to capture and do the above.
Thanks.
Got it...
In the build.bat file, append this to a build string:
... /p:customOutputDir="%1"
In MsBuild file:
<PropertyGroup>
<OutputDir>$(customOutputDir)</OutputDir>
</PropertyGroup>
Then OutputDir can be used in Targets.
Thanks.
Isn't the idea of an automated build that the build is repeateable and without user input?
But, i would guess that powershell has some better options for getting input from a user for this than standard dos.
Would it also be possible to query the user input before executing the build file and pass it as a param?