Showing posts with label Powershell. Show all posts
Showing posts with label Powershell. Show all posts

Saturday, April 30, 2016

Use Get-CommandVariable to auto generate variables for a command

In this post, I want to share a silly new PowerShell command Get-CommandVariable that I wrote and it is available on github as a powershell module and you can put inside your modules folder and start using it.  “What problem this is trying to solve?” you might be thinking.

You start writing a powershell script and you want to pass variables to a command.  For example, you want to create a new AzureRM Web app using command New-AzureRMWebApp. Then you will have to write your command like this using $variables.

$ResourceGroupName = "mycustomresourcegroup"
$Name = "webapp01"
$Location = "centralus"

New-AzureRMWebApp  -ResourceGroupName $ResourceGroupName -Name $Name -Location $Location

Let’s understand what you had to go through. Type all the parameters and its variables with a $sign.  Copy those variables at the top so you can initialize them. That’s not the tricky part. The thing that gets me the most is trying to think names for those variables and then copy the variables at the top.

So Get-CommandVariable will create this all for you.  How?  Just specify for which command you want to generate automatic $variables and it will do it. In the below example, we are using New-AzureRMWebApp command and after it generates the output,  I just copy the text and put it inside ISE window.

PS C:>Get-CommandVariable –CommandName New-AzureRMWebApp
$ResourceGroupName = ""
$Name = ""
$Location = ""
New-AzureRMWebApp -ResourceGroupName $ResourceGroupName -Name $Name -Location $Location

If you are using powershell for quite a long time you may know that powershell commands have certain parameters as mandatory and others are not mandatory.  Then there are commands that will work different combination of parameters. And Get-CommandVariable will work in those scenarios as well.

For example, if you provide –ShowAll option it will generate variables for all the parameters not just mandatory ones.

PS C:>Get-CommandVariable –CommandName New-AzureStorageAccount -ListParameterSets
$StorageAccountName = ""
$Label = ""
$Description = ""
$AffinityGroup = ""
$Type = ""
$Profile = ""

New-AzureStorageAccount  -StorageAccountName $StorageAccountName -Label $Label -Description $Description -AffinityGroup $AffinityGroup -Type $Type -Profile $Profile

You can specify to list all the ParameterSets (think: different combinations of parameters) that a particular command expects. For example,

PS C:>Get-CommandVariable –CommandName New-AzureStorageAccount -ListParameterSets
Name
----
ParameterSetAffinityGroup
ParameterSetLocation

Then you can tell Get-CommandVariable to generate variables for a particular ParameterSet. In the below example we are chosing ParameterSetAffinityGroup
PS C:>Get-CommandVariable –CommandName New-AzureStorageAccount  -ParameterSetName ParameterSetAffinityGroup
$StorageAccountName = ""
$AffinityGroup = ""

New-AzureStorageAccount  -StorageAccountName $StorageAccountName -AffinityGroup $AffinityGroup
By default, I am showing just the mandatory parameters, since my goal to get going as quickly as possible. To show all parameters you will have to provide –ShowAll flag. So that’s it and if you think this might be useful to you then give it a try. You can provide feedback in the comments below or submit issues directly on github as well.

Sunday, April 3, 2016

PowerShell Tip: Start-Transcript and Stop-Transcript

You are trying to write a powershell script but you don’t know all the right comands to execute and what parameters to pass.  So you write a bunch of commands, out of which many don’t work and some work. After lots of experimentation you finally find the right commands with right parameters that would work for your script. You can do Get-history to get a list of all the commands that were executed. But that history only gets persisted as long as you have the powershell window open. Once you close the window that history is gone. I have closed the console window many times and found myself cursing for having done so because I couldn’t remember those commands. Now I have to again fiddle with those commands. Wouldn’t it be nice if there was something that would record everything you did in a text file? Once you are done experimenting you could tell it to stop recording your session and then you can use that text file for later reference. 
Start-Transcript and Stop-Transcript does just that. Before you start experimenting just tell PowerShell you want to record stuff in a text file and Start-Transcript will do that.  After you are done you can use Stop-Transcript to stop recording.  I like this feature. But there is more.
See in the Start-Transcript you have to provide a txt file and I don’t like providing path information with a uniquename everytime I want to do Start-Transcript and I want it to be automatic.  Below function will create a uniquename of the file based upon a timestamp. Now if you wish to provide a meaningful name then you can do that too. The script will append a unique timestamp to that name.  You can put this function into your profile and it will be available to you when the console loads.
function start-recording {
param(
 [string]$sessName
)
try { stop-transcript } catch {}
$uniqFileName = (get-Date).ToString('MMddyyyyhhmmss');
if([System.String]::IsNullOrEmpty($sessName)){
 Start-Transcript -Path "C:\Scripts\Transcripts\$uniqFileName.txt" -NoClobber 
}
else{
Start-Transcript -Path "C:\Scripts\Transcripts\$sessName$uniqFileName.txt" -NoClobber  
}
}

set-alias stop-recording stop-transcript 
I invoke command -  Start-Recording “webpackge” and a unique file name is created in my scripts/transcripts folder by that name.  The function also does stop-transcript if you execute the function start-recording again so it does saves existing session and starts recording another one. 

Wednesday, February 12, 2014

Work with TF.exe from Visual Studio Package Manager Console

I work with Team Foundation Server and I have found sometimes it takes lots of mousing [I mean mouse movements] to accomplish a certain task.  While if you don’t know then all the commands that are available to us from Visual Studio are also available from the command line.  Oh! wait it is the Visual Studio command prompt. So there is just one thing you will have to do in order to make TF.exe available from almost everywhere.  Add the following path to your system environment variable and TF.exe will be now accessible from the command prompt. 

C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE

[This is the path for Visual Studio 2013 installed on my machine. If you are using VS2012 then change 12.0 to 11.0 version]

After this you can just do like this Win + R + cmd and Enter.  Then just type TF /?

image

Since this is available from normal command prompt what would prevent us from opening PowerShell and try there. 

image

Oh! It works there.  Btw this was my favorite approach to work with Team Foundation Server until I thought what if this would work from Visual Studio Package Manager Console which is using PowerShell. 

image

Awesome. So why I like this approach? If I am working on something in regular PowerShell and to find pending changes I have to navigate to my working directory to do tf status.  But with Package Manager Console I can just do tf status and it gives me pending changes for just that project and that particular branch that I am in.  Try doing pwd it will give the current location of your project.  And since docking in VS is supported for all the windows I just dock Package Manager Console to another monitor.

I use regular PowerShell and Package Manager Console for committing changes to TFS interchangeably. 

Sunday, February 9, 2014

PSBuild makes working with MSBuild.exe easier from PowerShell

PSBuild is a project started by Sayed Hashimi to simplify working with MSBuild.exe from PowerShell. It is hosted on gitHub.  I really like this project because I love PowerShell and if I can do things in PowerShell then I am more inclined to use PowerShell first. 

PSBuild installs into your Modules folder and is available once you install it by the following very simple command. I love projects that go one extra mile to make setup as easier as possible.  Open PowerShell and copy the following command and paste into console by just right clicking your mouse.

(new-object Net.WebClient).DownloadString("https://raw.github.com/ligershark/psbuild/master/src/GetPSBuild.ps1") | iex

Once you have installed it, you can list all the commands available by this command.

Get-Command –Module PSBuild

image


The most important command we are interested here is Invoke-MSBuild cmdlet.

Get-Help Invoke-MSBuild

There are lots of examples on how to use Invoke-MSBuild

Get-Help Invoke-MSBuild -Examples

image


MSbuild is very powerful and rightly so as it is used to build visual studio projects.  With Visual Studio 2013 MSbuild is being shipped with Visual Studio and is now referred to as MSBuild 2013. Previously it was shipped with .Net framework and versioning was also different now the version is bumped up to 12.0 from 4.0 to match with VS2013 (it is 12.0). It also comes as separate package known as Build Tools Package. This allows you to install the Build Tools Package as standalone and create a light weight build server. Path to MSBuild has also been changed and Invoke-MSBuild makes it easier for us to not know these different versions and hunt around for paths.  If you want to get MSBuild.exe then you can use one of the cmdlets as


Get-MSbuild


image


Another one to check the versions


& (Get-MSBuild) /version


image


If you want to get the MSBuild.exe help then


& (Get-MSBuild) /help


As you have noticed,  you can invoke MSbuild like this if you want but Invoke-PowerShell is better because you don’t have to second guess the syntax of properties and target that you will specify.  You pass properties as hash table. Let’s do something interesting here.


I am going to create an empty asp.net mvc website and try to build it and then publish to IIS website using a publish profile.


image


Simple call to build the project.
PS C:\BadSourceCodes\demoWebApp\demoWebApp> Invoke-MSBuild .\demoWebApp.csproj


image


You can get the latest log file


PS C:\Get-PSbuildLog


And if you want to open the log file then


PS C:\Open-PSBuildLog


Since this is a web project you can setup web publishing inside visual studio.  You can see these series of article on how to do web publishing here. I have created a website in IIS7 and setup Web Deploy publishing for that website.  Thanks to Sayed for his blog post - I was able to publish my website to the IIS website I by passing these parameters.


>Invoke-MSbuild .\demoWebApp.csproj –DefaultProperties (@{'Configuration'='Release';'DeployOnBuild'='true';'PublishProfile'='Default Settings';'username'='myusername';'password'=’password';'AllowUntrustedCertificate'='true'})


image


With PSBuild you can create a script that you can schedule to auto run on your build server that will automatically build the project and deploy the web project to IIS website.


Go check out the project on github and happy automating.

Thursday, September 19, 2013

Use Processmonitor to debug command line syntax passed through PowerShell

If you are trying to find out why a certain formatted command within PowerShell is not working then I strongly recommend using ProcessMonitor tool from SysInternals suite of tools.  When you initially fire up the ProcessMonitor then it will show you lots of information and it can be overwhelming. 
image_thumb[1]
So there is a powerful filtering functionality built into the utility.  Click on the filter tab and click on filter.
image_thumb[3]
Then on the filter section add two filters as shown in the figure below.
image_thumb[7]
The first filter is Path is “C:\Program Files\7-zip\7z.exe” and second one is Operation is “Load Image”.  After applying the filter and running my PowerShell script I can narrow down my desired line.
image_thumb[9]
So if next time any command line utility is behaving weird then debug using ProcessMonitor.

Sunday, August 25, 2013

Quickly explore 159 samples of windows 8 SDK using PowerShell and touch on Surface Pro

I was looking sample source code on windows 8 and I found this collection of 159 windows 8 app samples in C#. You can also find windows 8 and 8.1 app samples in c#, vb.net, C++ and Javascript. Many windows 8 developers might be aware of it already but it becomes painful after some time to go and open each solution. However, I think most people only open a particular solution when the need arises. However, I wanted to explore all of them (159 of them), run, and see what all the different features Windows 8.1 and 8 SDK has. That way I am at least aware of the features and use them in my application whenever needed. The only problem is opening each of them through the explorer window and locate .sln file three layers deep. One more thing I am doing this all on a surface pro sitting while on a couch.

Part of my frustration with these samples was, first you have to download them samples (that’s ok), unblock them since they are downloaded from internet (fine by me), then navigate to the solution folder and open the .sln file (not good for 159 of them). There is a lot of context switching when I launch the application from Visual Studio and then the modern interface opens up another context switch. Now try to do this on a Windows 8 pro tables like the Surface Pro while sitting on your couch or lying in the bed. Too much hassle right, but I have figured out an easy way to overcome this problem.  My Answer to the problem is Powershell. I have attempted to solve this problem before(link) but it was not as elegant as this one is.
Check this powershell one-liner


I extracted windows8 samples to the folder C:\windows8samples. This one-liner first finds all the .sln files in the samples folders, then selects just the name and fullname property of the the .sln file, and then sorts them by name, and then shows you list of .sln files in a gridview window. If you are running windows 8 for viewing all the windows 8 samples, you have powershell v3. In PS v3, you can pass the object(s) you select from the gridview down to the on going pipeline. You can select a single object which what I have done here because I do not want to open multiple solutions. This is done by the –Outputmode single property. After selecting the object in the gridview interface you click ok and then I use invoke item command to open the file in the default program configured for the file.

Ok so what's simple in this as you might ask right. Copy this one-liner, create a powershell script named OpenVSSolutions.ps1, and create a shortcut on the desktop. Open the properties of the shortcut file and change the target field in the property window to this

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NonInteractive C:\Scripts\OpenVSSolutions.ps1



This will hide powershell window and present you gridview with all solutions and then you can select one solution and hit ok. It will open visual studio. Done. Then just run the application and explore. To ease surfing source code inside visual studio 2012 or 2013 is switch scroll to use map mode scrolling (now supported in VS2013 and productivity power tool for VS2012).

You can change icon of your script too to reflect something else than powershell icon. Mine looks like the following.

You can place the shortcut on the toolbar or pin it to start page.

Now that is simple and easy solution to browse all samples quickly using touch on a Surface Pro lying in the bed when you don’t want to sit at your desk.

This solution works for other purposes too. I have folder named C:\BadSourceCodes\ which contains ad-hoc solutions I create to quickly test a concept. Over a time the folder contains lots of solutions and finding them is also pain.  Instead I use above technique to open solutions.  I have couple of such folders with lots of Visual Studio solutions some contains samples from various books I have purchased over the years, my production source code library, badsourcecode library and others.  To make our experiment more generic lets change our one line of powershell to replace the hardcoded path with a $path variable.   Save the script file.

Now you can create multiple shortcuts for your folders and edit target property of each shortcut.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NonInteractive C:\Scripts\OpenVSSolutions.ps1 “C:\BadSourceCode"

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NonInteractive C:\Scripts\OpenVSSolutions.ps1 “C:\Windows8Samples"

Pin different shortcuts to your taskbar and now you easily open any solution without having to navigate different folders, switch context and later forget what you were trying to do.

Bonus. Download samples of C#, Linq101 samples, windows 8 and others to create a shortcut menu like this.

This is my first attempt at creating gifs but if I click or double tap on any of the above shortcuts on my desktop then it displays a list of visual studio solutions and I can open any one of it.










Wednesday, November 14, 2012

Create a Default IIS Website using PowerShell

First you will have to import module named "WebAdministration"

PS C:\> Import-Module "WebAdministration"

PS C:\> Get-Command -Module "WebAdministration"

 You can create new website, app pools, bindings with these commandlets. I have combined few commandlets and created my script which does the following:

1.  Does a remote session into a specific computer with specific credentials
2.  Imports Module WebAdministration on that server.
3.  Creates WebAppPool and set's its property.  I have parameter where you can specify .net framework.
4.  Tests physical path of the website if it doesn't exists then it creates one for you.
5.  Create Website with bindings and set's application pool to the one created in step 3.

Here is the script. Enjoy


With PowerShell V3 I do Show-command createdefaultwebsite.ps1 name and it shows me nice form to fill in the details.  I love this feature in V3.


Monday, July 30, 2012

Launching Visual Studio solutions made easy with PowerShell


In my previous post on “Launching Apps with PowerShell Functions”, I showed you how to launch a visual studio solution.  That was very easy I guess because we used the start command and provided full file path with a .sln extension. It started visual studio to open this file.

I am going to extend this solution to solve one of my pain points with Visual Studio Start Page.  On VS start page there is a section for “Recent Projects” which lists recently opened visual studio solutions.  In that section you can pin a solution which you like VS2010 to remember.  It is a great feature to open your most used visual studio solutions.  However, when you get into creating a lot of visual studio projects for development, testing some random code and production.  Over a time this list gets unmanageable and it won’t remember too many projects.  You don’t want to pin all these test projects and bad projects you have created because they will conflict with your frequently pinned production solutions.  Those test solutions might be good for future testing purposes.  Again you don’t want them to go away either.  So I wanted to list all the solutions that I have created in my two or three directories over time and use start command to open it.  There could be more solutions elsewhere but I am not interested in those.  So here is my solution:

I have create a script which will search two or three interested directories like C:\BadSourceCode, C:\ProductionSourceCode, C:\Testing etc for .sln file and list them.  Then I will provide a number to launch a particular visual studio solution I am interested in.  This way I don’t have to wait in the morning to wake up my visual studio from sleep and search and find the solution I want to open.  Ouch!! That is too much of work for me when I am half asleep.  Instead I just want to fire up PowerShell and supply visual studio solution number that I want to open and bang it just works while I sit back and relax.

So here is the script.  You can put the entire script in your profile or store it in a location and create powershell function to run that script.

Write-Host " " 
Write-Host "Listing all visual studio solutions" -ForegroundColor yellow
Write-Host " " 
Add-Type @'
public class solution
{
public int id;
public string name;
public string fullname;
}
'@
$tempSolutions = Get-ChildItem C:\Testing,C:\Production,C:\BadSourceCodes -Recurse -Include *.sln | select Name, FullName 
$si = New-Object solution
$st = [Type] $si.GetType()
$base = [System.Collections.Generic.List``1]
$qt = $base.MakeGenericType(@($st))
$so = [Activator]::CreateInstance($qt)
for($i = 0; $i -lt $tempSolutions.Count; $i++)
{
$obj = New-Object solution
$obj.id = $i
$obj.name = $tempSolutions[$i].Name
$obj.fullname = $tempSolutions[$i].FullName
$so.Add($obj)
}
$so | Format-Table -AutoSize
$choice = Read-Host "Enter a number to open a vs solution or letter x to exit: " 
if($choice -eq "x"){
Write-Host -ForegroundColor red "exited selection" -BackgroundColor black
}
else
{
Write-Host -ForegroundColor Yellow "Opening VS solution:" $so[$choice].name
Start-Process $so[$choice].fullname
}

The above script will recursively list all .sln files and then create a custom object for each file and then add it to a list object.  To launch a particular solution, we provide a number to get that object from a list and use the fullname property of the object to the start command to open that solution inside visual studio 2010.

I have created this little function and inside this function I provide the path to the script file for launching solutions.


function get-vssolutions {
C:\scripts\OpenVSsolutions.ps1 
}


PS C:\> get-vssolutions 



Enter a number to open a vs solution or type letter x to exit: 

Friday, July 20, 2012

Launching Apps with PowerShell Functions


Launching of apps is much easier with windows 7 taskbar.  The "pin to taskbar" option when you right click any open software will pin the item to the taskbar.  I have lots of remote desktop sessions pinned on remote desktop connection. However, there are some situations where launching some softwares it is quicker through your keyboard than through the mouse.  AutoHotkey is very popular when you want to assign shortcut keys for launching apps and trigger some custom actions.  However, I have fallen in love with the PowerShell way of launching some of my favorite and most used apps on windows.   I have found it very useful and so I would like to share them with you.

Launch Remote Desktop Connections with PowerShell 
Put this PS function in your PS profile and replace server10 with your server name in the following command and see remote desktop connection open up on its own.  For the first time, you will have to enter the password.  I love this very much.  


function rmd {
param([string]$computername)
& "C:\windows\system32\mstsc.exe" /v:$computername /fullscreen
}

PS> C:\>rmd server10

Search about a PowerShell error on Google from PowerShell
Suppose you had an error when you were working inside powershell and you had no idea what that error was and you thought that a Google search might result an answer.  At this point you don’t want to copy the error object or text and then open or switch to Google and type.  This function takes care of searching on Google for you.  It will search for the last error [$error[0]] encountered .  

Check this function out and if you like it then put it in your powershell profile.  Replace the chrome.exe path with your path or it might be just replacing Goofy with your name.


function Get-ErrorInfo

& "C:\Users\Goofy\AppData\Local\Google\Chrome\Application\chrome.exe"  https://www.google.com/search?q=$error[0].Exception
}

Let’s do some dumb mistake and try to search it on Google.

PS C:\> get-comand19


The term 'get-comand19' is not recognized as the name of a cmdlet, function, script file, or operable program. Check th
e spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:13
+ get-comand19 <<<<
    + CategoryInfo          : ObjectNotFound: (get-comand19:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS C:\> get-errorinfo

Just search on Google with PowerShell
If you observed the command carefully then you know how to search on Google through powershell.  Here is my simple function to search on Google.


function g { 
param([string]$searchstring)
& "C:\Users\Goofy\AppData\Local\Google\Chrome\Application\chrome.exe" https://www.google.com/search?q=$searchstring
}

Launch volume control pop-up with PowerShell
I have a bad habit of experimenting something in powershell (more on that later).  However, I found this link on superuser.com about open the volume control with a command.  Try this command “sndvol.exe –f” in run command window (open it by typing win+r) and you can see volume control but again when working in PowerShell I want to do it from PowerShell. Sweet little no brainer PS function. 

function vol { & "C:\windows\system32\sndvol.exe" -f}

Hit vol and see volume control pop-up.  Use up and down arrow key to control vol (obviously).
Launch a Visual Studio solution with PowerShell
Start a visual studio solution from PowerShell.  Yes, I know that you can pin your most frequented items to task bar but I wanted to do it in powershell. I have created a small PowerShell function to start my daily project that I am working on. I don’t if people would find it useful or not but I do find it useful in my work life.  


function vs {
param([string]$document)
switch($document){
"pm"{
start "C:\sourcecode\dailyprojectIworkOn.sln"
}
}
}

Launch a word document with PowerShell
Ah! So easy once you know how the start cmd works right. Just put it in front of any document and it will open in relevant software for you. One of the things I have opened while working on a project is having a word document opened up.  It is like my live journal of that project.  Over a time my word document get very large and I have to do two things: 1) Open word doc 2) Scroll to the last page 3) Save it before exiting.  For two and and three I have created a word macro which will scroll the doc to the last page and save it for me before I exit.  Finally for launching that word document I do this.

function show{
param([string]$document)
switch($document){
"pm" {
start "C:\project\projdoc.docm"
}
}
}
So you see I don't panic if the start menu is gone from windows 8.  I only have to remember the PowerShell way of launching apps.  If you have some other PowerShell functions that you put in your PowerShell profile then share with me in the comments sections.

Sunday, July 8, 2012

Using Powershell Function to Quickly Test Regular Expressions

I have found the easiest way to learn and experiment with regular expressions using Powershell.  In this function below, I have converted .Net code to test regular expressions into PowerShell Function test-regex.  It accepts two parameters $inputString and $regex.  In $regex you provide the regular expression you want to test.  Put this little snippet into your PowerShell profile so that you don’t have to run this function everytime.  PowerShell Profile will run this function everytime you open a new PowerShell session.  This MSDN article explains on how to put functions into a PowerShell profile.


function test-regex {
param(
[string]$inputString,
[string]$regex
)
$match = [System.Text.RegularExpressions.Regex]::Match($inputString,$regex)
Write-Host $match.Value -foreground "Yellow" 
}

This allows me to quickly test regular-expressions in powershell without much hassle. And if you want to find help about regular expressions inside powershell then you can type following command and get all help related to regular expressions.

>PS help about_regular*

I refer “Regular Expression Language Reference” MSDN article to understand more about regular expressions.  

In the below example, I wanted to extract Distinguished Name (DN) from a list of thousands of users from a text file.  Each line in that text file had a Unique DN.  So I resorted to Regular Expression to match DN in each line.   Check out the sample text which has DN in bolded text.

Some Random Text in which our DN is located so here is our Distinguished Name: CN=jsssmith2,OU=allUsers,OU=Payroll Users,DC=Com2,DC=myCompany,DC=com.  Everyline has this DN and we want to extract DN from every line.

I want to show how we can easily test this using powershell function we created. Copy the above text and paste in powershell using just right click.  Check out the syntax to test the function.


Our input string and regex are in double quotes above.  You can see the matching text in Yellow color.  The nice thing about this is if you get it wrong then iterating is easy because you just have to hit the up arrow on your keyboard and just change the regex part and it will give you another match.  

The matching regular expression was “CN.*DC=com”.  The neat thing in this approach I like is not remembering any software to open to test regexes.  Since I have PowerShell as my first item on my task bar I hit Win+1 and start testing.  I hope you like this approach.  If there is any easier approach or quicker way to test regular expressions then please let me know about it in the comments sections.

Monday, June 25, 2012

Passing Parameters to Remote PowerShell Session Inside a Script


Let’s take a look at the following script named md.ps1:

When I run using below syntax it will not work as expected on a remote computer.

PS> .\md.ps1 “computer1” “Release” “ReleaseBuildConfig”

It will replace $BuildDefinition variable but then it will not list all its child folders.  Further more it will not replace $configuration variable with the value, for example, “Release” or “Debug”.   As soon as I hard coded them the rest of the command worked just fine.  Ok so what was the issue.

It turns out that when you are trying to pass parameters to a script and then invoke a remote powershell session and access variables inside that PSsession then you have to declare them twice in your scripts. Yes Twice.  Check the final script.

It was painful to figure it out and my co-worker friend helped me figure it out.

Tuesday, June 19, 2012

Deploying Website and Windows Service using MSDeploy, Powershell and InstallUtil from TFS Build


We have a visual studio solution which has 4 projects into it.  One of them is a web project, another windows service project, class library and console application, all under source control inside Team Foundation Server 2010 in one team project.
TeamProject
   Solution.sln
   >Web Project
   >Windows Service Project
   >Class Library Project
   >Console Application Project

When we queue a new build using Team Foundation Build then all the content gets dumped into one single drops folder (a shared folder on your staging server).   It is a total mess and we need to have our structure as shown above on the destination drops folder.  We would like to have control over the structure of the destination files dropped by TFS Build.  I was able to achieve this by following this guideline posted on MSDN “Control Where the Build System Places Your Binaries”.  


First we will take a look into how to deploy Web Application using Web package in TFS Build using powershell.  Alright now when we look into the drop folder for our website on the destination folder we can see a _publishedpackages folder which has a .zip file.  We are interested in this folder.  We will grab the .zip file which is our package and deploy it using MSdeploy.


MSDeploy is a command line utility used to deploy webpackages to IIS.  All good but there is one problem and that is everytime a new build is queued a new folder is created with date and version number appended to it.  Our files are inside dropsfolder\builddefinition\latestfolder.6192012.1\.  Inside this folder if you have customized Process template then each project will have its own folder and inside that there will be Release\Debug folder.  Then we have to get the path to our .zip file for the website which we need to pass to MSDeploy to publish website to IIS.  This can get cumbersome and slow down our cycle. Why? Everytime a new build is dropped you will have to go through this pain.

1. Login into the server
2. Fire up command line
3. Locate the path to the latest build folder and find the path to the _PublishedPackages folder
4. Pass those parameters into MSDeploy and then run it.

What we want is combine steps 2 to 4 into one powershell script and store it inside TFS that will do the job for us.  You will have to modify process template to run a powershell script during build process. Check this excellent article from Ewald Hofman on how to execute powershell script from tfs. One might think that there are different ways of achieving same functionality and automating it directly from build definition itself but I learnt something in this process which I thought might be useful to somebody.


$latestbuildfolder = Get-ChildItem "C:\dropsfolder\BuildDefinition\" | Sort-Object LastWriteTime -Descending | SELECT-Object -First 1 $webpackagelocation = "\ProjectName\WebProject1\Release\_PublishedWebsites\WebProject1_Package\WebProject1.zip" $finalstring = $latestbuildfolder.FullName + $webpackagelocation & 'C:\Program Files\IIS\Microsoft Web Deploy v2\msdeploy.exe' -verb=sync -dest=auto "-source=package=$finalstring"

In the first three lines we try to grab the path to the web package and then pass the path to MSDeploy commandline utility to deploy to IIS web server.

Line1: First we list all the folders using Get-ChildItem and then sort them in descending order and get the first item.  This first item is our last build that was queued.  Everytime when you run the script it will make sure we get the latest folder.
Line2: This line is very simple because for all the future builds we know where our .Zip file will be sitting.  So it is direct path to the zip file.
Line3: Just concatenating two strings from line1 and 2 but be careful here the $latestbuildfolder variable holds the folder as the item so to get the path you need $latestbuildfolder.FullName.
Line4: If you want to run commandline utilities through powershell then you will have to first put “&” ampersand varialbel then include commandline utility in quotes and then provide other variables or arguments in quotes.  MSDeploy is little bit tricky here.  You provide all other variables in without quotes and include the –source=package-$finalstring in quotes.  To find out the exact syntax that worked, I played with lots of combinations and eventually a friend of mine in the office helped me achieve this.  You can also use Join-Path cmdlet of powershell to join paths.

Now you can do almost anything now with MSDeploy, TFSBuild and Powershell.  MSDeploy is a very powerfull tool and the one that is less exploited for deploying Websites guess.
Let’s try to install windows service using Powershell so that this wil happen automatically everytime.  We also have to take care of one more problem, and that is uninstalling windows service everytime we deploy a new version.  Below is my powershell script to uninstall windows service:


$service1 = Get-WmiObject -Class win32_service -Filter "Name='OurServiceName'" 
 if ($service1.State -eq 'Running') 
 { 
 Write-Host "Stopping $service1.Name service" stop-service OurServiceName & 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /u $service1.PathName
 } 


In the above script, I am using Get-WMIObject win32_service to get our windows service because Get-Service was not giving me the path to the executable that was used to install the service.  The executable path to the service is revealed only through win32_service instance of the class.   This way we don’t have to cycle through our drops folder and find previous versions of windows service.
Let’s take a look at our installation powershell script code:

$latestbuildfolder = Get-ChildItem "C:\dropsfolder\BuildDefinition\" | Sort-Object LastWriteTime -Descending | SELECT-Object -First 1
$windowsServiceFolder = "\ProjectName\WindowsService\Release\WindowsService.exe"
$exepath = $latestbuildfolder.FullName + $windowsServiceFolder
& 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /username=companyname\srviceaccount /password=password1 $exepath
Start-Service -Name OurServiceName
$getlatestservice = Get-Service -Name OurServiceName
$getlatestservice


In the above code pay close attention to the installutil.exe code where we specify /username and /password because we don’t want to have that user prompt annoy us everytime we try to install our windows service.  For this to work you have to make some changes to your windows service installer code.  I have used code to make it work.  There is an appropriate BeforeInstall event where you need to add this code because there are BeforeInstall events for ServiceProcessInstaller and ServiceInstaller objects.

Now putting it all together we want to put our powershell script into TFS as we don’t want to have it on the debug or production machine.  We want it under source control.


$session = New-PSSession -ComputerName Server20 
Invoke-Command -Session $session -ScriptBlock { $service1 = Get-WmiObject -Class win32_service -Filter "Name='OurServiceName'" 
 if ($service1.State -eq 'Running') { 
 Write-Host "Stopping $service1.Name service" stop-service OurServiceName 
& 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /u $service1.PathName 
 } 
$latestbuildfolder = Get-ChildItem "C:\dropsfolder\BuildDefinition\" | Sort-Object LastWriteTime -Descending | SELECT-Object -First 1 
$webpackagelocation = "\ProjectName\WebProject1\Release\_PublishedWebsites\WebProject1_Package\WebProject1.zip" 
$finalstring = $latestbuildfolder.FullName + $webpackagelocation 
& 'C:\Program Files\IIS\Microsoft Web Deploy v2\msdeploy.exe' -verb=sync -dest=auto "-source=package=$finalstring" 
$windowsServiceFolder = "\ProjectName\WindowsService\Release\WindowsService.exe" $exepath = $latestbuildfolder.FullName + $windowsServiceFolder 
& 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /username=companyname\srviceaccount /password=password1 $exepath 
Start-Service -Name OurServiceName $getlatestservice = Get-Service -Name OurServiceName $getlatestservice 
}

The first two lines are of importance we are running these command using powershell remoting (Powershell remoting should be enabled on the remote server).  We are create a new session specify computer name and run our powershell commands.  These commands will run on the remote machine just like they would run on a client machine.  We are all set.  Queue a new build and see this magic happen.

One more step closer to continuous integration!!!