Wednesday, June 6, 2012

Runas /netonly

I discovered the runas /netonly gem today and it is wonderful! Yes, wonderful!

The runas command allows you to run a program on a machine as a different user than what you are logged in as. This is great when you are doing same domain activities, but what about crossing domains? This is the issue the /netonly switch solves. If you include this switch, it will run the program as your logged in user, but any network calls will be sent as if they came from the user you specified! Thus, if I execute something like:

runas /netonly /user:AnotherDomain\AnotherUser devenv

This runs Visual Studio as my user, but any network calls (TFS, Database, etc.) will use AnotherDomain\AnotherUser. A SQL server with Windows Authentication only can now be connected to via this command on your machine not on the same domain as the SQL server. You can run code locally in Visual Studio and debug, connecting via Windows Authentication to a server on a different domain.

Yes, this is wonderful!

Tuesday, May 8, 2012

Config Transforms for Elements

I would have thought this was obvious, but it took me a bit to figure it out.  The config file transforms that are available for web.config files and for all other files can be used to replace sections of a config file, based on a project configuration.  Most of the examples show changing attributes.  I wanted to change the entire element, in this instance, connectionStrings.  To do this, you simply put the Replace value in the Transform attribute on the element:

Sunday, May 6, 2012

Troubleshooting Build Failures

We are having random build failures which have been a huge pain to troubleshoot (although we know that it has something to do with multi-core builds), so I wrote a quick Powershell script today that will run the build against the solution repeatedly until the build fails.
while($true)
{
    C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /nologo /target:Rebuild YourSolutionFile.sln /p:SkipInvalidConfigurations=true /p:DeployOnBuild=False /p:Configuration=Debug /p:RestorePackages=false /m:2 /p:OutDir="E:\play\Binaries\\"  | Out-Host
 
    if ($LastExitCode -ne 0)
    {
        break;
    }
}

Tuesday, April 10, 2012

Crossing Domains without Password Pain

I work in consulting, and as such, my machine is never on the client domain.  This causes some headache because every time I want to connect to a client resource, I am prompted for my client username and password.  By default, the client’s resources are in the Internet zone, so nothing is trusted, nor are my saved passwords used to authenticate – I am asked to reenter my password each time I connect to any resource.  Fortunately, there is an easy solution – simply add the domain as a Local Intranet site in your Internet settings.  Under Internet Options, Security, Local Intranet, Sites, Advanced, you can enter the paths to the client resource that you commonly access.  Once this is done, after you check the “Save Password” option when you access a resource again, you won’t have to reenter the password again.

If the client has a TFS instance, you can save your username and password used to access it by going to the team web access site in Internet Explorer and saving your credentials when prompted.  When you launch Visual Studio and connect to that TFS instance, it will then use your saved credentials.

Monday, April 9, 2012

Setting up a new IIS Server for ASP.NET or MVC

When setting up a new Windows Server for hosting ASP.NET or MVC applications, I have several Powershell scripts that I run to modify some of the default IIS settings.  You can also modify the IIS settings manually, but don’t fear the command line – it is your friend.

Powershell has an IIS module that you will need to import to run most of these commands – WebAdministration.  Now for the first set of scripts:
Import-Module WebAdministration
 
#expire web content after 30 days
Set-WebConfigurationProperty -filter "/system.webServer/staticContent/clientCache" -name cacheControlMode -value "UseMaxAge"
Set-WebConfigurationProperty -filter "/system.webServer/staticContent/clientCache" -name cacheControlMaxAge -value "30.00:00:00"
 
# change logging to include two more properties
Set-WebConfigurationProperty -filter "/system.applicationHost/sites/siteDefaults/logFile" -name logExtFileFlags -value "Date, Time, ClientIP, UserName, ServerIP, Method, UriStem, UriQuery, HttpStatus, Win32Status, BytesSent, BytesRecv, TimeTaken, ServerPort, UserAgent, HttpSubStatus"
 
# change the IIS server's header value to from value -- applies to ENTIRE SERVER
$computer = gc env:computername
Set-WebConfiguration  -filter "/system.webServer/httpProtocol/customHeaders/add[@value='ASP.NET']/@name" -value "From"
Set-WebConfiguration  -filter "/system.webServer/httpProtocol/customHeaders/add[@name='From']/@value" -value $computer

The above scripts are mostly self-explanatory – adjusting logging, static caching, and making sure the HTTP header of the sites on the box will include the box name.  This is especially useful in load-balanced scenarios, when you need to troubleshoot an errant server.

The next script modifies IIS to allow anonymous and windows authentication to be set in the web.config of child applications.
# change the master IIS config file to allow override of anonymous and windows auth
[xml]$config = Get-Content C:\Windows\System32\inetsrv\config\applicationHost.config
$config.selectSingleNode("/configuration/configSections/sectionGroup[@name='system.webServer']/sectionGroup[@name='security']/sectionGroup[@name='authentication']/section[@name='anonymousAuthentication']").SetAttribute("overrideModeDefault", "Allow")
$config.selectSingleNode("/configuration/configSections/sectionGroup[@name='system.webServer']/sectionGroup[@name='security']/sectionGroup[@name='authentication']/section[@name='windowsAuthentication']").SetAttribute("overrideModeDefault", "Allow")
$config.Save("C:\Windows\System32\inetsrv\config\applicationHost.config")

By default IIS does not allow child applications to define their own authentication.  You can change a site’s security policy in the IIS manager, but this modifies the security settings in the applicationHost.config file instead of the web.config of the application.  You can allow the local site’s web.config to define this with the script below:

And finally, I prefer IIS to be clear of any default sites and application pools before I start adding my own, so I remove them (Warning: this will clear all sites and application pools from a server):
# RESET IIS environment
Remove-Item 'IIS:\AppPools\*' -Recurse
Remove-Item 'IIS:\Sites\*' -Recurse

Thursday, April 5, 2012

Assembly Implicit References, Installations, and the MSBuild Impact

I spent quite a bit of time trying to figure out why two of our Silverlight XAP files did not include the System.Windows.Controls.Toolkit.Internals.dll file when packaged on a build server.  Of course, when I built these locally, the XAP files were packaged correctly with this assembly being included in the XAP package.  I finally ran the exact same MSBuild command on both servers and then compared the output to determine why it was not being included.  When the Silverlight Toolkit is installed, it puts the reference assemblies under C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Bin.  We have not installed the toolkit on the build server; we have just added the assemblies to source control under a shared path.  What I found is that if the assemblies are not in their “default” location, when MSBuild compiles the code, it won’t copy implied assemblies to the bin directory of the project, which will then prevent these from being included in the XAP file.  In this example, System.Windows.Controls.Toolkit.dll references System.Windows.Controls.Toolkit.Internals.dll, but the latter is not referenced in the project.  If I compile on a machine that has the Toolkit installed, the latter assembly will be copied to the bin.  On a machine that does not have the toolkit installed, the Internals assembly will not be copied to the bin.

Interesting compile behavior.  Lesson learned: make your references explicit, especially for Silverlight projects.

Tuesday, April 3, 2012

Creating and Modifying MSMQ’s Using Powershell

There are a few Powershell functions that I have written and use to create and delete queues in MSMQ and to modify permissions on these queues.  At the end of the script there are a few examples on how to use the functions.
Clear-Host
[Reflection.Assembly]::LoadWithPartialName("System.Messaging")
  
function Create-Queue
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$queueName,
        [switch]$isPrivate = $true,     
        [switch]$isTransactional = $false
    )
      
    $innerQueueName = $queueName
      
    if ($isPrivate)
    {
        $innerQueueName = ".\private$\"+$queueName
    }
    else
    {
        $innerQueueName = ".\"+$queueName
    }
      
    if (![System.Messaging.MessageQueue]::Exists($innerQueueName))
    {
        [System.Messaging.MessageQueue]::Create($innerQueueName, $isTransactional) 
        Write-Host "Created queue " $innerQueueName
    }
    return $innerQueueName
}
  
function Add-QueueUserPermission
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$queuePath,
        [Parameter(Mandatory=$true)]
        [string]$user,
        [string]$fullControl = $false
    )
      
    $q = new-object System.Messaging.MessageQueue($queuePath)
      
        if ($fullControl -eq $true)
        {
            Write-Host "FullControl granted to "  $user
            $q.SetPermissions($User, [System.Messaging.MessageQueueAccessRights]::FullControl, [System.Messaging.AccessControlEntryType]::Allow) 
        }
        else
        {
            Write-Host "Restricted access granted to "  $user      
            $q.SetPermissions($User, [System.Messaging.MessageQueueAccessRights]::DeleteMessage, [System.Messaging.AccessControlEntryType]::Set) 
            $q.SetPermissions($User, [System.Messaging.MessageQueueAccessRights]::GenericWrite, [System.Messaging.AccessControlEntryType]::Allow) 
            $q.SetPermissions($User, [System.Messaging.MessageQueueAccessRights]::PeekMessage, [System.Messaging.AccessControlEntryType]::Allow) 
            $q.SetPermissions($User, [System.Messaging.MessageQueueAccessRights]::ReceiveJournalMessage, [System.Messaging.AccessControlEntryType]::Allow)
        }
      
      
}
  
function Delete-Queue
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$queuePath
    )
      
    [System.Messaging.MessageQueue]::Delete($queuePath)
}
  
function Delete-Queues
{
    [System.Messaging.MessageQueue]::GetPrivateQueuesByMachine($Env:COMPUTERNAME) | ForEach-Object { Delete-Queue($_.Path) }
    [System.Messaging.MessageQueue]::GetPublicQueuesByMachine($Env:COMPUTERNAME) | ForEach-Object { Delete-Queue($_.Path) }
}
  
#########################################################################################################################
  
#examples
# creates a queue that is private but not transactional
#Create-Queue -queueName "My Queue" -isPrivate $true -isTransactional $false
  
# grants restricted access to the queue
#Add-QueueUserPermission -queuePath ".\private$\My Queue" -user 'domain\account'
  
# grants full control to the queue
#Add-QueueUserPermission -queuePath ".\private$\My Queue" -user 'domain\account' -fullControl