.NET 4.0 Runtime Superceded by .NET 4.5!

by John 1. March 2012 10:53

After Installing Visual Studio 11, I was going to upgrade my internal tooling to also provide the ability to point to .NET 4.5, so that I could consider my installation complete.  Starting with Visual Studio 2010, I started explicitly choosing which MSBuild (and other supporting files) to use via specifying the paths to the assorted binaries.

 

  

Much to my surprise, there was not a .NET 4.5 Framework Directory!  After some digging, I discovered that ".NET 4.5 Supercedes .NET 4.0"! This means that the new Runtime replaces the 4.0 runtime.  The 4.0 directory where the files are installed are as follows:

C:\Windows\Microsoft.NET\Framework\v4.0.30319
C:\Windows\Microsoft.NET\Framework64\v4.0.30319

Next, I am going to rebuild all of the code using Visual Studio 2010 to make sure nothing new creeps in when Visual Studio 11 and the new 4.5 Framework is installed.

 

 

Tags:

Net40 | Net45

Windows 8 built-in Hyper-V Support - How do I know if I can use it?

by John 29. February 2012 13:06

One of the awesome new features in Windows 8 (Client) is the built-in support of Hyper-V!  This means you can run multiple desktop operating systems without the pain of constantly reinstalling or re-imaging.  This is particularly useful if you are like me and always like to try the latest and greatest.

I read the description about the support, and it indicated that your processor needs to support SLAT; that was a new term for me so I dug around and found an easy way to tell.

Here is a link to a TechNet Article that describes how to know!

https://social.technet.microsoft.com/wiki/contents/articles/1401.aspx

In the article, it talks about CoreInfo, this is a SysInternals tool (http://technet.microsoft.com/en-us/sysinternals/cc835722) that provides you with insight as to what virtualization support is supported by your processor (and enabled in the BIOS).  After downloading it and running it..

  

YES!  Now I just need to have some downtown to upgrade my desktop to Windows8...

Tags:

Windows8

Visual Studio 11 Beta is out!

by John 29. February 2012 11:58

At last!  We can start talking about some of the cool new stuff in Visual Studio 11.  As many of you know, I have been busily working on Guidance that also has finally shipped!  Anywhere, where are some useful links to directly download both Windows 8 and Visual Studio 11!

http://blogs.msdn.com/b/visualstudio/
http://blogs.msdn.com/b/b8/archive/2012/02/29/welcome-to-windows-8-the-consumer-preview.aspx

Something else of note, with TFS11, there is a new version called TFS 11 Express, which is free for up to 5 users!  The users have to be “named” users, but it does provide a way to play with the product, without purchasing in.

-=-
Below is a list of all of the newly published Guidance, for your reference.  Many are still applicable to 2010 and 2008.

http://blogs.msdn.com/b/visualstudioalm/archive/2012/02/29/welcome-to-visual-studio-11-alm-rangers-readiness-beta-wave.aspx

The link above provides a link to the ALM Rangers page that has the release information.

I can be found listed as a contributor in several materials from these groups:

Visual Studio Architecture Tooling Guide
Visual Studio Lab Management Guide
Team Foundation Server Process Template Customization Guide
Team Foundation Server Upgrade Guide (reviewer)

Tags:

Rangers | TFS | VS11 | Windows8 | TFS11

Database Installation using VCDBCMD and WIX

by John 29. November 2011 11:03

The solution I am talking about in this blog posting can be downloaded here:  http://blogs.kungfucoder.com/downloadmaterials/dbdeployment.rar

You will need to download and install Wix (at least Version 3.5 RTM newer).

 


I created a sample solution to start as a conversation point for the database deployment; (please feel free to comment, trash, or otherwise use).  This is patterned somewhat after the way we deploy things (although I don’t generally provide the MSI files and such as part of the deployment to customers – but maybe that is something I should consider).  What I tried to create here is a full example of building and deploying a database using the AdventureWorks sample database.

I tried to keep it as clear and minimal, yet functional as I could.  It also includes some of the other difficulties I had when I first started using Wix, as far as the pre-reqs, I actually include a bootstrapper in mine that installs other things like the .net framework, etc because it is bundled as a package (I left the pre-reqs in for the moment).

The installer generates the upgrade script (which for new databases includes a database-create), and executes it, the call to the VSDBCMD could easily be modified to only generate the resulting script (in fact, if you look In the resulting database installer directory, you will see the script is generated, and then run from within VSDBCMD).

This MSI solution allows you to deploy the database to basically any computer.  I use this type of installer when I deploy to my internal test environments that run as part of the TFS Lab Management Environments.  The same installer can be used to deploy as part of a client install as well. By using VSDBCMD, the deployer can either create a new database everytime, or upgrade a database in-place.

One thing that is probably worth mentioning is that some of the other team I work with do not include a database server inside their lab, instead that have a beefy database server and they assign database names with a version number, like “AdventureWorks_1_88_7381_0” for example, and the script then restores a well-know database, and applies the installer to it.

When I deploy this to a TFS Lab Environment, I generally include it in a batch file, this is because I copy all of the installation materials locally to the computers in the lab, and from there execute the installers.  However, the MSI could be run directly from the Deployment tab in the DefaultLabTemplate (VS2010) of the wizard that simply passes the desired parameters for the server name (and optionally the database name).

The Deployment tab could contain something as simple as this:

Msiexec /I $(BuildLocation)\Install.AdventureWorks.msi DATABASESERVER=SomeServer\SomeInstance DATABASENAME=AdventureWorksCustomName


 

Solution Contents

AdventureWorksLT2008 – Database project reverse engineered from the SQL 2008 Adventure Works Download (from here: http://msftdbprodsamples.codeplex.com/releases/view/37109)


Fragment.AdventureWorks – This install component contains the files that are associated with the actual database

Fragment.Support.DatabaseInstaller – this install component contain the files that are associated with VSDBCMD; I included the supporting files needed when the computer that the resulting MSI is executed on does not have the SQL management studio installed

Install.AdventureWorks – this is the actually installer that uses the two fragments to generate a database installer

Shared – this contains the various binaries, including the dbSchemas for 2005 and 2008 (I am still looking for one for 2008R2).

This is what the project looks like this when you open it (It is not bound to any version control):

 

Installer

The installer takes two parameters currently:

DATABASESERVER – Name of the Database Server (and Instance) to install on
DATABASENAME – Name of the resulting database

I defaulted them both as . for the server name (local) and the database name of AdventureWorks.

This installer assumes that the user running it has enough rights on the database server to create a new database if needed; that is, the installer the way it is does not require that the database be pre-created.


You can run this with parameters with a command line something like this: 

Msiexec /I Install.AdventureWorks.msi DATABASESERVER=SomeServer\SomeInstance DATABASENAME=AdventureWorksCustomName

I could easily add a UI to this if you want, as well as some other options.

After installing, you get this in the control panel.

 



If you uninstall, it only removes this entry from the control panel, it does not do anything to the database, this is by design, I don’t think you want an installer to remove a database that could potentially have customer data in it.

Wix Fragments

The WiX Fragments within this solution basically only carry the files needed for the installer.  I chose to break the installer up to make it more modular and clear to those reading this Blog.  Fragment.AdventureWorks takes the files that are generated from the AdventureWorksLT2008 database project and incorporates them into a single Component that can be used within an installer. 

When I create these fragments, I try to choose Ids that are unique enough to indicate the database being carried, this is because often I will need to install more than one database, so the installer can actually reference and deliver multiple databases if needed.

Of particular note, it is important to select the 'Bind Files into the library file'; this ensures that the consumer of the Wix Library will have the files needed/referenced.

 

 

Fragment.SupportDatabaseInstaller carries with it all of the supporting files needed for VSDBCMD.  I chose to copy the files into the Shared project so that they are both versioned, and do no rely on the build computer to have everything installed in specific locations.  Generally speaking I also include any other needed restribitutables this way as well; most of my work is specific client-based work so this is generally not an issue.  The Support.DatabaseInstaller fragment contains two components, one for the Installer files and one for the SQL CE Runtime.

The goal of this installer was to provide enough of the supporting files so that the database could be installed from a computer that does not have the SQL Management tools installed on it.

Installer

The Wix Installer ended up actually being fairly simple.  

Definition of parameters that can be applied to the MSI to control where the database is installed, the parameter names are DATABASESERVER and DATABNAME; included are the custom actions that copy the parameter to local variables inside the WiX Installer. (you could opt to use the parameters directly, but I tend to like the parameters and properties like this).

<!--
    **************************************************************
       Command Line Parameters, and Actions to Retrieve and User them
    **************************************************************
    -->

    <!-- Command Line Parameter:  APPSERVER HOST NAME -->
    <Property Id="DATABASESERVER" Value="." Secure="yes"/>
    <Property Id="DATABASENAME" Value="AdventureWorks" Secure="yes"/>
    
    <CustomAction Id='SetTargetDatabaseServer' Property='TargetDatabaseServer' Value='[DATABASESERVER]'  />
    <CustomAction Id='SetTargetDatabaseName' Property='TargetDatabaseName' Value='[DATABASENAME]' />

Install/Uninstall Actions, on installation we want to run the database installer VSDBCMD.

 

<!--
    **************************************************************
       Install/Uninstall Actions
    **************************************************************
    -->
    
    <!--Define the custom action to build the vsdbcmd.exe command line string-->
    <CustomAction Id="SetLaunchVSDBCMDCommandLine"
                  Property="LaunchVsdbcmd"
                  Value=""[#VSDBCMD.exe]" /a:Deploy /cs:"Server=[TargetDatabaseServer];Integrated Security=true;" /dsp:Sql /dd+ /manifest:"[#db.AW_MANIFEST]" /p:TargetDatabase="[TargetDatabaseName]""
                  Execute="immediate"/>
    <!--Define the custom action to execute vsdbcmd.exe-->
    <CustomAction Id="LaunchVsdbcmd" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="yes"/>

Of particular note is the VSDBCMD command line:

/dsp:Sql indicates that we will be using the SQL provider for the installation

/dd+ indicates that the database should be modifed (there are options to only create the sql required to perform the action)

/manifest - I confess, the installer really should have been in the fragment, because here we actually are referencing the AW_MANIFEST file contained inside the Fragment.AdventureWorks

<!-- FROM the Fragment.AdventureWorks project -->
          <Component Id="db.manifest.AW" Guid="*">
            <File Id="db.AW_MANIFEST" Source="$(var.DatabaseScript_PATH)\$(var.AdventureWorksLT2008.ProjectName).deploymanifest" KeyPath="yes"/>
          </Component>

 

Notice also that I specify the TargetDatabaseServer and the TargetDatabaseName on the command line of VSDBCMD.

Finally, the definition of the execution sequence of the custom actions

<!--
    **************************************************************
       Define the Execute Sequence for all of the custom actions
    **************************************************************
    -->
    
    <!-- Install/Uninstall Actions -->
    <InstallExecuteSequence>
      <Custom Action='SetTargetDatabaseServer' After='LaunchConditions' />
      <Custom Action='SetTargetDatabaseName' After='LaunchConditions' />
      <Custom Action="SetLaunchVSDBCMDCommandLine" Before="InstallFiles"/>
      
      
      <Custom Action="LaunchVsdbcmd" After="InstallFiles" >
        <![CDATA[&ProductFeature=3 AND NOT REMOVE]]>   <!--Only run this custom action if a certain feature is being installed--> <!-- NO Action on Uninstall -->
      </Custom>      
      
    </InstallExecuteSequence>

Notice, that we will only launch VSBDCMD if ProductFeature is being installed.  Product Feature is defined as follows inside the installer. As you can use, this specific Feature contains the Database, along with the Installer and supporting files.

 

<!--
    **************************************************************
      Product Feature Definitions
    **************************************************************
    -->
    
    <Feature Id="ProductFeature" Title="AdventureWorks Database" Level="1">
      <ComponentGroupRef Id="Database_AW" />
      <ComponentGroupRef Id="Database_Installer" />
      <ComponentGroupRef Id="Database_Installer_Support" />
    </Feature>

 This is not the perfect way to implement this solution, but by breaking things apart, hopefully how this all works will become clearer to those who need to install databases using WiX.  I recently downloaded the SSDT from Microsoft, I believe by the time this gets posted, CTP4 will be widely available.  This is the next generation of Database Project support for Visual Studio, and is support in both VS2010 and dev11.  It looks like the VSDBCMD.exe is being replaced by a new set of command line utilities.  I'll post another entry in the future that has the same level of detail using the newer technology.

 

 

 

 

Tags:

WiX | Database

Changing the WorkItem type for Build Failure

by John 14. October 2011 13:44

Lately, we have been doing a lot of changes to our internal processes... something we noticed lately is that it would be useful to track the build-breakages seperately from bugs.  While they are bugs, they tend to require different attention that "regular" bugs (priority-wise, and sometimes even staff-wise).

Ultimately, we decided to treat a build failure as a different work item type so that it doesn't clutter the bug reporting and management that we do on a day-to-day basis.  As such, the simpliest way to do this was to clone the 'bug' work item type to make a 'build failure' Work Item Type. 

1. Export the Bug WorkItem Type

2. Rename the XML File exported to BuildFailure.xml

3. Modify the BuildFailure.xml to have a new Name/Description

<WORKITEMTYPE name="Build Failure"> <DESCRIPTION>Describes a failure that occurs during the Build and Build Verification Processes.</DESCRIPTION>
 

4. Import the new Work Item Type

5. Update your build process template, locate this snippet of code (in my template it was at about line 250)

<mtbwa:InvokeForReason DisplayName="Create Work Item for non-Shelveset Builds" Reason="Manual, IndividualCI, BatchedCI, Schedule, ScheduleForced, UserCreated"> <mtbwa:OpenWorkItem AssignedTo="[BuildDetail.RequestedFor]" Comment="["This work item was created by TFS Build on a build failure."]" CustomFields="[New Dictionary(Of String, String) From { {"System.Reason", "Build Failure"}, {"Microsoft.VSTS.TCM.ReproSteps", "Start the build using TFS Build"}, {"Priority", "1"}, {"Severity", "1 - Critical"} }]" DisplayName="Create Work Item" Title="[String.Format("Build Failure in Build: {0}", BuildDetail.BuildNumber)]" Type="["Bug"]" /> </mtbwa:InvokeForReason>

    Change the work item type to be 'Build Falure', or whatever name you specified in Step 3 above.

6. Update the Categories to indicate that Build Failure is a type of Category. 

  • Export your categories
witadmin exportcategories /collection:%Collection% /p:%Project% /f:"C:\categories.xml"
  • Modify the resulting categories.xml file as follows:
<CATEGORY name="Bug Category" refname="Microsoft.BugCategory"> <DEFAULTWORKITEMTYPE name="Bug" /> <WORKITEMTYPE name="Build Failure" /> </CATEGORY>
  • Import the update category list
witadmin importcategories /collection:%Collection% /p:%Project% /f:"C:\categories.xml"

I generally place some of the more common commands in a batch file, then have another that sets up the target.  In this case, the target-setting batch file would have contents like this: 

Set Collection=http://TFSServer:8080/tfs/MyProjectCollection 
Set Project=MyProject

You can create a query that returns work items by category, for example after you have performed the above steps, a query that would return all of the Build Failures and bugs would look like this:

 

 

After we started using this for a while, we decided to change the Assigned To field so that it could contain the value of Not Assigned.  We also changed the Transition to the Resolved state to set the Assigned to to Not Assigned; the default behavior was to set it to the creator, but this would set it to the TFS Build Service Account, and that user is not part of the 'assignable' users.  We'll probably come back and revist this at a later time...

 

Tags: , ,

TFS

Remote Debugging into an Isolated Lab Environment

by John 11. October 2011 20:04

As part of correcting issues in an isolated lab environment, it is often necessary for developers to connect to one or more machines within the isolated network.  While the concepts of remote debugging are pretty straight forward, I decided to go ahead and put together something that our developers could use as a guide to perform those types of activities.  Our codebase is 100% managed code, for whatever reason, it is more difficult to get attached and debugging across domains with managed code than it is with Native code.  Honestly, I don't understand why that is, but it can be a bit frustrating unless you fully understand how to get such an environment configured properly.

The first thing you need to do is install the Visual Studio Remote Debugging tools (x86 or x64) on the target machine.  The install is pretty straight forward, so I won't go into that directly.  We actually include the remote debugging monitor (MSVCMON) as part of our base lab images.  In our case we don't configure the tool to run as a service, instead, we place a short cut on the desktop so that it can be started when needed.

Once of the key aspects of remote debugging is that you have to use the same user account on your developer machine, as well as the target machine.  This means Local Accounts on both machines.  So the first step is to create a local account on your desktop, lets call it "Remote.Debugging" for our purposes.  This account doesn't necessarily need to be in any special local groups.  Create the same user name and password on the target machine.  This time, however, you need to put the account in the target machines Administrators group (the local administrators group, not the domains).

Once you have the accounts created, you need to update the permissions in the Visual Studio 2010 Remote Debugger. Once opened, use Tools->Permissions and add the account as part of the security.  Below is a screen shot of what you should see when you do this.

 

 

Now you are ready to attach and debug, should the need arise. Notice that the user signed into the desktop is from the isolated domain called "LAB", and the user name is Lab.Administrator.

When you need to debug, simple hold the shift key down and right-click on the Visual Studio 2010 Remote Debugger Icon on the desktop, you will get a pop up menu that looks something like this:

 

Notice that there is a option for 'Run as Different User'.  Select that, and you are presented with a credentials pop up window as follows:

  

 

It is important that you enter the local machine name in the windows credential dialog (because the account is local to this machine. A little known shortcut is that you can use the "." as the machine name for local accounts.

  

 Once you have successfully authenticated, you will have a Monitor window like the one below.  Notice that the user name now indicates Remote.Debugging@LabDesktop01.

 

 

This means that you are now ready to connect from Visual Studio.  On your workstation, follow the same process, hold the shift key down and right-click on the visual studio shortcut, you will see a pop up menu like this

 

 

 As before, select Run as Different user.  When prompted enter the credentials of the local account 'Remote.Debugging'.  If this is the first time you are performing this procedure, you will be asked the various initial-setup questions, such as your default environment settings, etc.

Generally, I open the solution that has the executable that I am debugging so that I have access to all of the source code within the environment.  If you are opening a project that is corrected to TFS, you will get a pop up asking you for your TFS credentials.  This is normal because you are running as the local user you created. Once you have opened the solution, and are ready to attach, select Debug->Attach to Process.

Enter the machine name (you know the long with that starts with LAB_), or the IP Address and press enter.  Once attached, within visual studio, you will get to select the process.

 

 

 You can select the "Show processes from all users" checkbox to see the other processes.  Once you find your process select it and click Attach.  Notice that the "Attach to" field currently indicates Automatic: Native Code; If you have connected as outlined here, this will change when you select your process to whatever is suitable.  Over in the Lab, the Visual Studio Remote Debugging monitor shows this

 

 

 Notice that both screens show that you are attaching as the user created specifically for this purpose.

And now a brief word about security...  Since many organizations have multiple developers, I recommend that you disable the account you created on your developer workstation until you need it for debugging.  This helps prevent unauthorized access to your computer.

 

 

Tags: , ,

Debugging

Coded UI Testing - Could not load Visual Studio Test Assembly

by John 11. October 2011 15:31

Today we started running our Coded-UI Tests in the TFS Lab Environment, or at least tried to... unfortunately, we were greeted with this popup error message while attempting to run tests...

  

 

As it turns out, we have written several of our own automation objects, and expanded some of the automation peers to be able to test for some of the extended features that we have within our user interfaces.  The Offending assemblies are:

Microsoft.VisualStudio.TestTools.UITesting
Microsoft.VisualStudio.TestTools.UITest.Extension

These are referenced by our "automation" assemblies.  We do not want to include the Microsoft TestTools files in our internal build/test because we want to test what matches the deliverables supplied to our customers.  I had some difficulty tracking down an idea solution to this, I had thought about updating our deployment scripts to copy the needed files into the appropriate directories, but discarded that idea because I want to maintain an environment as close to what the end users would have as possible.

I came across a post in the MSDN news groups talking about adding a probing path to the configuration, but decided against that because I did not want to include specific paths in the application's config file that could change from environment to environment; not to mention would deviates from what we deliver to the customer.  Instead I decided to include an assembly resolver that could determine where the requested assemblies were and resolve them when encountered. It just seemed much cleaner that way.

I updated our WPF application to include the resolver as follows

//-- Connect an assembly resolver to allow the Code to run within a Test Environment
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolverForTestEnvironment);

I had previously come across a post by Vinoth Sathappan (Microsoft), he is a Software Design Engineer in the Visual Studio 2010 Coded UI Test Team that had posted a snippet of code that could be used to resolve the assembly.  I adapted his code to work on both 32-bit and 64-bit systems, as well as in an environment where visual studio, itself is not installed.

private static Assembly CurrentDomain_AssemblyResolverForTestEnvironment(object sender, ResolveEventArgs args)
{
  AssemblyName assemblyName = new AssemblyName(args.Name);
  if (assemblyName.Name.StartsWith("Microsoft.VisualStudio", StringComparison.Ordinal))
  {        
    string path = string.Empty;

    //-- Lets try to get the installation location of the public/private assemblies from the Visual Studio key...
    if (Environment.Is64BitOperatingSystem)
    {
      path = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\10.0\\", "InstallDir", null);
    }
    else
    {
      path = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0\\", "InstallDir", null);
    }

    //-- Did we get it? 
    if(string.IsNullOrEmpty(path))
    {
      //-- No, perhaps the Quality Tools Agent is installed instead...
      if (Environment.Is64BitOperatingSystem)
      {
        path = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\10.0\\EnterpriseTools\\QualityTools\\Agent\\", "InstallationPath", null);
      }
      else
      {
        path = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0\\EnterpriseTools\\QualityTools\\Agent\\", "InstallationPath", null);
      }
    }

    //-- Do we have an installation directory?
    if (!string.IsNullOrWhiteSpace(path))
    {
      //-- Yes, lets get to see if the assembly exists in either the public or private assemblies directories...
      string assemblyPath = Path.Combine(path, "PublicAssemblies", string.Format(CultureInfo.InvariantCulture, "{0}.dll", assemblyName.Name));

      if (!File.Exists(assemblyPath))
      {
        assemblyPath = Path.Combine(path, "PrivateAssemblies", string.Format(CultureInfo.InvariantCulture, "{0}.dll", assemblyName.Name));

        if (!File.Exists(assemblyPath))
        {
          string commonFiles = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86);
          if (string.IsNullOrWhiteSpace(commonFiles))
          {
            commonFiles = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles);
          }

          assemblyPath = Path.Combine(commonFiles, "Microsoft Shared", "VSTT", "10.0", string.Format(CultureInfo.InvariantCulture, "{0}.dll", assemblyName.Name));
        }
      }

      //-- If we have the full path of the assembly, lets load and return it...
      if (File.Exists(assemblyPath))
      {
        return Assembly.LoadFrom(assemblyPath);
      }
    }
  }

  return null;
}

The code above works in my Isolated Lab Environment when I have only installed the Lab, Build and Test Agents.  This is because it first checks the Visual Studio registry key, and if that is not found, it checks the quality tools registry key.

 

 

 

Tags: , ,

Testing

Returning an Exit code from a batch file

by John 10. October 2011 17:16

As you can probably tell by some of these posts, I have been working on automating some of the build-test tasks as it relates to deploying into a testing environment.  Today I found myself wondering how to fail a deployment workflow that has issues inside of a batch file.  As it turns out, this was quite easy...

Inside the batch file, when you detect a problem, you can execute a statement like the following:

exit /b 1

The /b tells the batch file processor that you do not want to exit the full command shell, but rather just termiante the batch file.  The number afterwards is the desired exit or errorlevel code.  A practical example of this functionality would be detecting whether or not the deletion of a file succeeded as follows:

del application.exe
if exist application.exe exit /b 1

The above would return from the batch file with the errorlevel set to 1

Tags: , ,

Detecting whether or not a Web Application Exists from within a batch file

by John 9. October 2011 18:34

In our environment, we use a series of batch files to help automate the creation of a uniform environment when a developer or tester gets a branch of code from TFS.  It has always been annoying to have to open up visual studio to create the various virtual directories, and it is been on my list a long time to clean that up.  This weekend, I had a little down time and decided to investigate just how to do that (Besides this has been on my list for a very long time).

It is probably worth mentioning that the way our environment is configured, developers and testers can all have multiple branches of the code locally on their computers, and they can switch to a new branch just by opening the solution.  Each branch has several solutions that all work together to form the product we are developing.

As part of our internal tooling, I have written a series of utilities that include batch files when appropriate to easily prepare a branch to work as a specific client-environment.  These activities include things like configuring Web Application Directories and project reference paths within the project files, along with a variety of updates to various configuration files to point to the correct WCF Services, Databases, and Directories.

Here is the batch file that works the "magic" of detecting whether or not the Web Application already exists, and if not creates it.

@ECHO OFF
   REM --------------------------------------------------------------------------------
   REM Check for and create WEBAPP under Default Web Site
   REM
   REM %1 is the WEBAPP to create
   REM %2 is the Physical path to the VDIR 
   REM --------------------------------------------------------------------------------

   IF "%1"=="" GOTO BadCommandLine
   IF "%2"=="" GOTO BadCommandLine

   REM Detecting if Web Application Already Exists...
   %windir%\system32\inetsrv\AppCmd.exe list app "Default Web Site/%1" | find "%1" > nul
   IF NOT ERRORLEVEL 1 GOTO AlreadyExists

   ECHO Creating %3 Web Application...
   %windir%\system32\inetsrv\AppCmd.exe ADD app /site.name:"Default Web Site" /path:/%1 /physicalPath:%2 >nul
   GOTO End

:AlreadyExists
   ECHO Virtual Directory already exists.
   GOTO End

:BadCommandLine
   ECHO.
   ECHO Web Application Name and Physical Path Required
   ECHO.
   ECHO CreateVirtualDirectory VirtualDirName PhysicalPath
   ECHO.

:END

 

The above batch file is called like this.

SET WebAppVDIR="WebService"
SET BinDirectory="C:\Projects\Test\test\bin"

REM Create Web Application...
CALL CreateWebApplication %WebAppVDIR% %BinDirectory%

The resulting call will create a Web Application (Using the Default Application Pool) that points to the specified Bin Directory "C:\Projects\Test\test\bin".

Hopefully, this will be useful to someone else. If you have questions or other ideas about how to improve upon this, please leave me a comment.

 

Tags: , ,

TFS Lab Management TF259050 and TF267061: What!?! But it was working last week!

by John 7. October 2011 14:20

After spending about a week working on some client issues, I was finally able to resume working on configuring a Lab environment to use multiple Isolated Networks for CI Builds (more on that in another future post once I am satifsfied I have it working properly).  When I restarted the Lab Environment (which was working last week), I received errors indicating that the TFS service account was now somehow invalid.

 

 

Much to my dismay, I spent several hours trying to figure out what the happened.  After searching around in all the usual places, I had come up empty.  I could tell it was going to be a long night...

Before I explain how I corrected this issue, a little additional background will probably be helpful.  The Hyper-V hosts in this particular environment are in a separate domain, lets call it 'XPServerFarm', and the "normal" servers all run in a separate domain; lets call it 'business.local'.  There is a two-way full trust between the two domains.  I suspect that it is a pretty normal configuration, although it does provide some challenges sometimes when it comes to permissions.

Reading the error message details, it looked to me like the TFS Service account did not have permissions on the host, I signed onto the host that has the VMs on it, and checked the local administrators group.  The account was in it.  Lots of things went through my mind at this point; but I'll refrain from sharing some of those thoughts and instead focus on some of the things I tried. 

Testing this involved shutting down the lab environment, and starting it multiple times; each time waiting for the environment to "settle" into a final state, often times this can take as long as 10 minutes depending on what is configured on the various lab VM machines.

I removed the account from the administrators group and re-added it; I don't know what I expected, but there was no change in the behavior.  I looked at the server and it had been running for about 70 days; I decided that I would reboot it (this server is only for testing so why not?); again no change.  Honestly, I really didn't expect the behavior to change...  Then I opened up Active Directory FSMO for XPServerFarm, which also happened to be the server that these VMs were on, and verified that the Trust itself was still intact.

At this point I was starting to get concerned... perhaps the problems were because it was on the AD FSMO, but I continued down the path of the verifying account issue itself.  Finally, I decided to log into the server as the TFS service account... there had to be something I was missing, configuration-wise, but why would it show up now?  I Remote Desktop-ed directly to the Hyper-V host computer and...

  

I reconnected as a local administrator on the Hyper-V host. Sure enough, the time had drifted to be about 12 minutes difference between the two domains.  Interestingly I had previously setup the time synchronization but apparently there was a problem.  I checked the configuration, and I found that the Time Remaining was 0 when I used the command

w32tm /query /peers

I waited a bit, and executed the command again... no change.  I updated the Time Service on the Hyper-V host as follows. I decided to include the details because I ran into an issue here as well (nothing is ever easy).

I unregistered the the time service using this command:

w32tm /unregister

Then I rebooted. When the host came back up, I re-registered the time service using this command

w32tm /register

Followed by attempting to start the time service 

Net start w32time

I could not get the service to start.  I was getting an error indicating that there was some sort of SID issue because the processes running in the svchost were using incompatible SIDs.  System Error 1290. I hunted around the internet and found some nonsense posts about changing the tapiserver's registry entry... instead I looked at all of the registry entries and discovered that they were all running with the same type of account (LocalSystem, LocalService or NetworkService); so that obviously wasn't it.  I rebooted because I remembered reading somewhere that that way svchost initializes itself includes some sort of registration for the processes running inside svchost.exe.  After rebooting, the time service started, and the problems were gone.

Now, I configured the time service to use the same time servers as the Business.Local domain using these commands

W32tm /config /syncfromflags:manual /manualpeerlist:”0.pool.ntp.org, 1.pool.ntp.org, 2.pool.ntp.org”
W32tm /config /reliable:yes
Net stop w32time
Net start w32time

Once completed, I issues these commands on both servers to make sure everything was working correctly.  

W32tm /config /update
W32tm /resync
Net stop w32time
Net start w32time

After waiting a few minutes, the time on the servers synced up and I was ready to continue. I went back to Lab Management Center and tried starting the environment.  After a period of time, the environment settled with this status:

 

 

After clicking Repair Testing Capability on the pull-down menu associated with the Testing Capabilities, I waited and was eventually greeted with a happy environment once again.

  

Tags: , ,

Lab Management | TFS

About the author

I am a software Entrepreneur, I always have been. I spend my days developing cool new software for several customers, mostly in the Justice space. I have been developing software for clients since I was 14 (I got addicted young). I am always up for a new challenge.

I love the bleeding edge; I have lots of battle scars to prove it.  I spend all of my extra time reading, researching and pushing the limits. Some call me a work-a-holic, to which I respond that there are not enough hours in the day to learn everything I want to know.  Some might say I am forever on a knoweldge quest.

Month List