Red Gate forums :: View topic - TFS Automatic Deployment and Email Script
Return to www.red-gate.com RSS Feed Available

Search  | Usergroups |  Profile |  Messages |  Log in  Register 
Go to product documentation

Deployment Manager PowerShell Scripts forum

TFS Automatic Deployment and Email Script

Search in Deployment Manager PowerShell Scripts forum
Post new topic   Reply to topic
Jump to:  
Author Message
SteveGTR



Joined: 11 Jan 2012
Posts: 66

PostPosted: Mon Dec 02, 2013 5:46 pm    Post subject: TFS Automatic Deployment and Email Script Reply with quote

Hopefully someone will find this useful. This script is called by a custom TFS process script after our website has been built and the package has been created on the NuGet feed. The xaml file is based on the code posted by Justin at http://thefutureofdeployment.com/calling-deploymentmanager-exe-tfs/. Instead of executing DeploymentManager.exe from the xaml, my version executes the following powershell script that performs the following tasks:

1) Executes DeploymentManager.exe

2) Archives old NuGet packages

3) Sends out an email message with annotated changes

With a little modification, this script might be useful for your project.

Code:

# To run via -File parameter do the following:
# Set-ExecutionPolicy Unrestricted

# Get the packages that are going to be deployed

$Feed = "\\servername\DeploymentFeed";
$Archive = "\\servername\DeploymentFeedArchive";

$WebPagePackages = $Feed + "\WebPage*.nupkg";
$MergePageDatabasePackages = $Feed + "\MergePage*.nupkg";

$CurrentWebPackage = Get-ChildItem $WebPagePackages | sort LastWriteTime | select -last 1;
$CurrentMergePagePackage = Get-ChildItem $MergePageDatabasePackages | sort LastWriteTime | select -last 1;

# Execute the deployment manager

&"C:\Program Files\Red Gate\DeploymentManager.EXE" create-release --server=http://servername:8080/ --apiKey=YourKey --project=ProjectName --deployto=Development --releasenotes="Automatically generated from TFS website build." --waitfordeployment --httpConnectTimeout=00:05:00 | Write-Host;

if ($LastExitCode -ne 0)
{
   Write-Host ("Deployment manager returned error: " + $LastExitCode);
   
   exit $LastExitCode;
}

# Move all files, but the latest to archive

if ($CurrentWebPackage -ne $null)
{
   Write-Host ("Current WebPage package: " + $CurrentWebPackage.Name);

   $Parts = $CurrentWebPackage.Name.Split(".");
   
   if ($Parts[4] -ne $null)
   {
      $WebPageBuildNumber = $Parts[4];
      $Version = $Parts[1] + "." + $Parts[2] + "." + $Parts[3] + "." + $Parts[4];
      
      Write-Host ("Current WebPage Build Number: " + $WebPageBuildNumber);
      Write-Host ("Current Deployment Version: " + $Version);
   }
   
   $ArchiveFiles = Get-ChildItem -Path $WebPagePackages -Exclude $CurrentWebPackage.Name;

   if ($ArchiveFiles -ne $null)
   {
      foreach ($File in $ArchiveFiles)
      {
         Write-Host ("Archiving: " + $File.Name);
         Move-Item $File $Archive | Write-Host;
      }
   }
}

if ($CurrentMergePagePackage -ne $null)
{
   Write-Host ("Current MergePage Database package: " + $CurrentMergePagePackage.Name);

   $Parts = $CurrentMergePagePackage.Name.Split(".");
   
   if ($Parts[3] -ne $null)
   {
      $MergePageBuildNumber = $Parts[0] + "_" + $Parts[2] + "." + $Parts[3];
      
      Write-Host ("Current MergePage Database Build Number: " + $MergePageBuildNumber);      
   }

   $ArchiveFiles = Get-ChildItem -Path $MergePageDatabasePackages -Exclude $CurrentMergePagePackage.Name;

   if ($ArchiveFiles -ne $null)
   {
      foreach ($File in $ArchiveFiles)
      {
         Write-Host ("Archiving: " + $File.Name);
         Move-Item $File $Archive | Write-Host;
      }
   }
}

if ($WebPageBuildNumber -ne $null -and $MergePageBuildNumber -ne $null)
{

   #---- Start of code to enumerate build details

   [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client"); 
   [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client");
   [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Common");

   if ((Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null)
   {
      Add-PSSnapin Microsoft.TeamFoundation.PowerShell;
   }

   $html = "<table>";

   $Packarray =
      @(
      [pscustomobject]@{PackageName="Website package: $CurrentWebPackage";ProjectName="MergePage";BuildName="MergePage";LocationToSearch="$/MergePage";BuildNumber=$WebPageBuildNumber},
      [pscustomobject]@{PackageName="Database package: $CurrentMergePagePackage";ProjectName="PageDatabases";BuildName="MergePage";LocationToSearch="$/PageDatabases/MergePage";BuildNumber=$MergePageBuildNumber}
   );

   foreach ($p in $Packarray)
   {
      $html +=
         "<tr>" +
            "<td colspan=4>" +
               $p.PackageName +
            "</td>" +
         "</tr>";

      $projectName =  $p.ProjectName;
      $buildName = $p.BuildName;
      $locationToSearch = $p.LocationToSearch;
      $buildNumber = $p.BuildNumber;

      $tfsCollectionUrl = "http://servername:8080/tfs/MergePageCollection";

      $server = new-object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(New-Object Uri($tfsCollectionUrl));

      $tfs = get-tfsserver $tfsCollectionUrl;

      $buildServer = $server.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer]);

      $buildDetail =
         $buildServer.QueryBuilds($projectName, $buildName) |
         where {($_.BuildNumber -eq $buildNumber -or ($_.BuildFinished -eq $true -and $_.Status -eq "Succeeded")) -and $_.LabelName -and $_.LabelName -ne "" } |
         sort -desc StartTime |
         select BuildNumber, StartTime, FinishTime;

      $vcs = $server.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]);

      $i1 = 0;
      $i2 = $i1 + 1;

      # Construct the date range using label dates

      $LabelDetail = $vcs.QueryLabels($BuildDetail[$i2].BuildNumber, $locationToSearch, $null, $false) | select LastModifiedDate;

      $dateRange = "D" + (Get-Date($LabelDetail.LastModifiedDate) -format s) + "~";

      $LabelDetail = $vcs.QueryLabels($BuildDetail[$i1].BuildNumber, $locationToSearch, $null, $false) | select LastModifiedDate;

      $dateRange += "D" + (Get-Date($LabelDetail.LastModifiedDate) -format s);

      $changeDetail = Get-TfsItemHistory $locationToSearch -Server $tfs -Version $dateRange -Recurse -IncludeItems | sort -desc ChangesetId;

      $changeFound = $false;

      # Because the Webpage building process is still active and hasn't finished, use the current date on the build
      # agent. This is the best I could do.
      if ($BuildDetail[$i1].FinishTime -eq $null -or $BuildDetail[$i1].FinishTime -lt $BuildDetail[$i1].StartTime)
      {
         $FinishTime = Get-Date;
      }
      else
      {
         $FinishTime = $BuildDetail[$i1].FinishTime;
      }

      $html +=
         "<tr>" +
            "<td colspan=2 nowrap>" +
               "Build: " + $BuildDetail[$i1].BuildNumber +
            "</td>" +
            "<td nowrap>" +
               "Started: " + (Get-Date($BuildDetail[$i1].StartTime) -format g) +
            "</td>" +
            "<td nowrap>" +
               "Duration: {0:N1} minutes" -f ($FinishTime - $BuildDetail[$i1].StartTime).TotalMinutes +
            "</td>" +
         "</tr>";

      foreach ($changeSet in ($changeDetail | select ChangesetId, Committer, CreationDate, Comment))
      {
         $changeFound = $true;
   
         $html +=
            "<tr>" +
               "<td>" +
                  "&nbsp;" +
               "</td>" +
               "<td nowrap>" +
                  "Changeset: " + $changeSet.ChangesetID +
               "</td>" +
               "<td nowrap>" +
                  "Date: " + (Get-Date($changeSet.CreationDate) -format g) +
               "</td>" +
               "<td nowrap>" +
                  "User: " + $changeSet.Committer +
               "</td>" +
            "</tr>";
         
         if ($changeSet.Comment -and $changeSet.Comment -ne "")
         {
            $html +=
               "<tr>" +
                  "<td>" +
                     "&nbsp;" +
                  "</td>" +
                  "<td colspan=3>" +
                     "Comments: " + $changeSet.Comment +
                  "</td>" +
               "</tr>";
         }
         else
         {
            $html +=
               "<tr>" +
                  "<td>" +
                     "&nbsp;" +
                  "</td>" +
                  "<td colspan=3>" +
                     "Comments: None" +
                  "</td>" +
               "</tr>";   
         }
   
         foreach ($typeSet in ($changeDetail | where { $_.ChangesetId -eq $changeSet.ChangesetID } | Select-Object -Expand "Changes"))
         {
            $html +=
               "<tr>" +
                  "<td>" +
                     "&nbsp;" +
                  "</td>" +
                  "<td>" +
                     "&nbsp;" +
                  "</td>" +
                  "<td colspan=2 nowrap>" +
                     "Action: " + $typeSet.ChangeType +
                  "</td>" +
               "</tr>";            
      
            foreach ($itemSet in ($typeSet | Select-Object -Expand "Item"))
            {
               $html +=
                  "<tr>" +
                     "<td>" +
                        "&nbsp;" +
                     "</td>" +
                     "<td>" +
                        "&nbsp;" +
                     "</td>" +
                     "<td>" +
                        "&nbsp;" +
                     "</td>" +               
                     "<td>" +
                        "Item: " + $itemSet.ServerItem +
                     "</td>" +
                  "</tr>";      
            }
         }
      }

      if ($changeFound -eq $false)
      {
         $html +=
            "<tr>" +
               "<td>" +
                  "&nbsp;" +
               "</td>" +
               "<td colspan=3>" +
                  "Nothing new included in build" +
               "</td>" +
            "</tr>";         
      }
   }

   $html += "</table>";
}
else
{
   $html = "Could not determine version information, check the log file.";
}

#---- End of code

# Email notification messages

$EmailFrom = "fromemail";
$EmailTo = "toemails";
$Subject = "Version: " + $Version + " deployed to Development";
$Body = $html;
$SMTPServer = "smtpserver";
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587);
$SMTPClient.EnableSsl = $true;
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("validemail", "password");
$Message = New-Object Net.Mail.MailMessage($EmailFrom, $EmailTo, $Subject, $Body);
$Message.IsBodyHtml = $true;
$SMTPClient.Send($Message);

exit 0;
Back to top
View user's profile Send private message
SteveGTR



Joined: 11 Jan 2012
Posts: 66

PostPosted: Mon Dec 02, 2013 9:14 pm    Post subject: Reply with quote

Here's an example of the information that is formatted in the email:

Code:

Subject: Version: 2.3.31.251 deployed to Development

Website package: \\servername\DeploymentFeed\WebPage.2.3.31.251.nupkg
Build: 251 Started: 11/29/2013 11:00 PM Duration: 16.3 minutes
  Changeset: 24226 Date: 11/29/2013 9:58 AM User: user1
  Comments: None
    Action: Edit
      Item: $/MergePage/WebPAGE/Reporting/Controls/RevisionList.ascx
Database package: \\servername\DeploymentFeed\MergePage.0.20131122.1.nupkg
Build: MergePage_20131122.1 Started: 11/22/2013 11:22 AM Duration: 2.4 minutes
  Changeset: 24206 Date: 11/22/2013 11:22 AM User: user2
  Comments: None
    Action: Edit
      Item: $/PageDatabases/MergePage/Stored Procedures/dbo.Admin_Generate_Delinquent_PPR_FFR.sql
    Action: Edit
      Item: $/PageDatabases/MergePage/Stored Procedures/dbo.Admin_Generate_Delinquent_PPR_FFR_Inprocess.sql
    Action: Edit
      Item: $/PageDatabases/MergePage/Stored Procedures/dbo.usp_GetDelinquentTTA.sql
    Action: Edit
      Item: $/PageDatabases/MergePage/Stored Procedures/dbo.uspGetOMBControlNoInfo.sql
    Action: Edit
      Item: $/PageDatabases/MergePage/Views/dbo.vwAttachment.sql
Back to top
View user's profile Send private message
justin.caldicott



Joined: 20 Apr 2011
Posts: 55

PostPosted: Tue Dec 03, 2013 12:39 pm    Post subject: Reply with quote

Thanks for sharing Steve, this looks very helpful!
_________________
Justin Caldicott
Product Manager - Deployment Manager
Red Gate
Back to top
View user's profile Send private message
benjamincain2@gmail.com



Joined: 30 Aug 2014
Posts: 5

PostPosted: Sat Aug 30, 2014 9:21 am    Post subject: Reply with quote

Because normally this is the way of understanding majority of factors so yeah and we do gain ideas about how things can be developed so following these terms goodly have us a view of moving ahead and we get to make a mark.
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic All times are GMT + 1 Hour
Page 1 of 1

 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group