Here at the office, we build a number of web applications. I recently started refactoring one such application to change and improve how we deploy it. As soon as I made the change to the .csproj to remove some old configurations and add new ones, I mysteriously started getting this error on our build machine (and only our build machine):
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.targets (2664): Filename: redirection.configError: Cannot read configuration file
After examining the changes I made, I figured "there's nothing that should be causing this. WTF?" Then after a while, I went on to our build machine and found that we didn't actually have IIS installed on the build machines (which is a good idea from a security standpoint, because there's really no reason for a build machine to have IIS installed). So why did this start suddenly ?
I went and examined the exact line that was causing the problem in the targets file, and the only thing that appeared to be including this conditional statement was the flag 'IncludeIisSettings'. Sure enough, the old configuration which we never used before and were using now had this set.
Thursday, July 30, 2015
Thursday, July 09, 2015
A problem with redirect loops in IIS (8.5) with ASP.NET MVC 5 (and Identity)
Ever see something like this ?
http://localhost:80/MyApp/Account/Login?ReturnUrl=%2FMyApp%2FAccount%2FLogin%3FReturnUrl%3D%252FMyApp%252FAccount%252FLogin%253FReturnUrl%253D%25252FMyApp%25252FAccount%25252FLogin%25253FReturnUrl%25253D%2525252FMyApp%2525252FAccount%2525252FLogin%2525253FReturnUrl%2525253D%252525252FMyApp%252525252FAccount%252525252FLogin%252525253FReturnUrl%252525253D%25252525252FMyApp%25252525252FAccount%25252525252FLogin%25252525253FReturnUrl%25252525253D%2525252525252FMyApp%2525252525252FAccount%2525252525252FLogin%2525252525253FReturnUrl%2525252525253D%252525252525252FMyApp%252525252525252FAccount%252525252525252FLogin%252525252525253FReturnUrl%252525252525253D%25252525252525252FMyApp%25252525252525252FAccount%25252525252525252FLogin%25252525252525253FReturnUrl%25252525252525253D%2525252525252525252FMyApp%2525252525252525252FAccount%2525252525252525252FLogin%2525252525252525253FReturnUrl%2525252525252525253D%252525252525252525252FMyApp%252525252525252525252FAccount%252525252525252525252FLogin%252525252525252525253FReturnUrl%252525252525252525253D%25252525252525252525252FMyApp%25252525252525252525252FAccount%25252525252525252525252FLogin%25252525252525252525253FReturnUrl%25252525252525252525253D%2525252525252525252525252FMyApp%2525252525252525252525252FAccount%2525252525252525252525252FLogin%2525252525252525252525253FReturnUrl%2525252525252525252525253D%252525252525252525252525252FMyApp%252525252525252525252525252FAccount%252525252525252525252525252FLogin%252525252525252525252525253FReturnUrl%252525252525252525252525253D%25252525252525252525252525252FMyApp%25252525252525252525252525252FAccount%25252525252525252525252525252FLogin%25252525252525252525252525253FReturnUrl%25252525252525252525252525253D%2525252525252525252525252525252FMyApp%2525252525252525252525252525252FAccount%2525252525252525252525252525252FLogin%2525252525252525252525252525253FReturnUrl%2525252525252525252525252525253D%252525252525252525252525252525252FMyApp%252525252525252525252525252525252F
As it turns out, IIS can generate redirect loops whenever there's a freshly deployed (installed) MVC application that hasn't had its application pool correctly setup.
Just thought I'd put this here in case anybody else runs across it.
http://localhost:80/MyApp/Account/Login?ReturnUrl=%2FMyApp%2FAccount%2FLogin%3FReturnUrl%3D%252FMyApp%252FAccount%252FLogin%253FReturnUrl%253D%25252FMyApp%25252FAccount%25252FLogin%25253FReturnUrl%25253D%2525252FMyApp%2525252FAccount%2525252FLogin%2525253FReturnUrl%2525253D%252525252FMyApp%252525252FAccount%252525252FLogin%252525253FReturnUrl%252525253D%25252525252FMyApp%25252525252FAccount%25252525252FLogin%25252525253FReturnUrl%25252525253D%2525252525252FMyApp%2525252525252FAccount%2525252525252FLogin%2525252525253FReturnUrl%2525252525253D%252525252525252FMyApp%252525252525252FAccount%252525252525252FLogin%252525252525253FReturnUrl%252525252525253D%25252525252525252FMyApp%25252525252525252FAccount%25252525252525252FLogin%25252525252525253FReturnUrl%25252525252525253D%2525252525252525252FMyApp%2525252525252525252FAccount%2525252525252525252FLogin%2525252525252525253FReturnUrl%2525252525252525253D%252525252525252525252FMyApp%252525252525252525252FAccount%252525252525252525252FLogin%252525252525252525253FReturnUrl%252525252525252525253D%25252525252525252525252FMyApp%25252525252525252525252FAccount%25252525252525252525252FLogin%25252525252525252525253FReturnUrl%25252525252525252525253D%2525252525252525252525252FMyApp%2525252525252525252525252FAccount%2525252525252525252525252FLogin%2525252525252525252525253FReturnUrl%2525252525252525252525253D%252525252525252525252525252FMyApp%252525252525252525252525252FAccount%252525252525252525252525252FLogin%252525252525252525252525253FReturnUrl%252525252525252525252525253D%25252525252525252525252525252FMyApp%25252525252525252525252525252FAccount%25252525252525252525252525252FLogin%25252525252525252525252525253FReturnUrl%25252525252525252525252525253D%2525252525252525252525252525252FMyApp%2525252525252525252525252525252FAccount%2525252525252525252525252525252FLogin%2525252525252525252525252525253FReturnUrl%2525252525252525252525252525253D%252525252525252525252525252525252FMyApp%252525252525252525252525252525252F
As it turns out, IIS can generate redirect loops whenever there's a freshly deployed (installed) MVC application that hasn't had its application pool correctly setup.
Just thought I'd put this here in case anybody else runs across it.
Thursday, June 04, 2015
Getting started with Azure in PowerShell
As it turns out, Azure has a ton of cmdlets available in the PowerShell command line to help you quickly and easily manage aspects of Azure.
- Install the Azure PowerShell from the Microsoft Web Platform Installer
- Install the Azure AD Module from the links on this page.
- After installing the Microsoft Online pack, you may have to copy the 'MSOnline' and 'MSOnlineExtended' folders from 'C:\windows\system32\WindowsPowerShell\v1.0\Modules' to 'C:\Windows\SysWOW64\WindowsPowerShell\v1.0\Modules' if you're running a server version of Windows
- Open a PowerShell session as Administrator
- Run the command "Import-Module azure"
- Run the command "Import-Module MSOnline"
- Add-AzureAccount -- Allows you to enter credentials and register your account with PowerShell so that you can manage it
- Get-AzureAccount -- shows you the currently active accounts
- Get-AzureSubscription -- shows the subscriptions available for the currently selected azure account
Also, to manage the roles in your Azure Active Directory, check out this page on Microsoft's Azure section.
To start log streaming for a specific web application, use this command:
PS C:\> Get-AzureWebsiteLog -Tail -Name mywebsitenamehere
Beginning in version 0.8.0, the Azure PowerShell installation includes more than one PowerShell module. You must explicitly decide whether to use the commands that are available in the Azure module or the Azure Resource Manager module. To make it easy to switch between them, we have added a new cmdlet, Switch-AzureMode, to the Azure Profile module.
When you use Azure PowerShell, the cmdlets in the Azure module are imported by default. To switch to the Azure Resource Manager module, use the Switch-AzureMode cmdlet. It removes the Azure module from your session and imports the Azure Resource Manager and Azure Profile modules.
To switch to the AzureResoureManager module, type:
PS C:\> Switch-AzureMode -Name AzureResourceManager
To switch back to the Azure module, type:
PS C:\> Switch-AzureMode -Name AzureServiceManagement
By default, Switch-AzureMode affects only the current session. To make the switch effective in all PowerShell sessions, use the Global parameter of Switch-AzureMode.
To start log streaming for a specific web application, use this command:
PS C:\> Get-AzureWebsiteLog -Tail -Name mywebsitenamehere
Sunday, May 31, 2015
Getting access to roles and group claims in Azure AD using the Graph API
Check this link out. It contains a tutorial on how to get Roles and Groups in Azure AD, so that you can have a single code-base for checking roles and groups regardless of whether you're using Azure AD or on-prem AD.
Also check out this link to an Azure AD example on GitHub for getting Group access in your applications
Also check out this link to an Azure AD example on GitHub for getting Group access in your applications
Tuesday, May 26, 2015
Implementing delegated authentication from an Azure MVC web app to an Azure Web API web service
Follow the information on this tutorial. Contained within that link, is this tutorial on authenticating a web app with delegated user identity to a web API service.
Authenticating users on an Azure web site (MVC) using Azure Active Directory (AAD)
Follow this guide provided on Github. It provides all of the information you need to enable the simplest form of Azure AD authentication in an MVC app. It can be used as the beginning step to getting a fully authenticated MVC web app / web API stack.
Monday, May 18, 2015
Creating an Intranet MVC 5 application using Windows Authentication that connects to a separate Intranet Web API 2 application also using Windows Authentication
Recently I wanted to create an Intranet MVC application using Windows Authentication that connects to a separate, pre-existing Intranet Web API 2 web service that also uses Windows Authentication. In order to get the Windows Authentication of the MVC application propagated to authenticate with the Web API 2 web service, I had to do the following:
1) Place the following in both applications' <system.web> sections in their respective configuration files.
4) Use the following to connect to Web API 2:
As a side note, apparently there's also a way to change the settings so that you don't have to modify the global aspnet.config file, you can do it on a per-AppPool basis. The technique is described here, though I've never tried it myself.
1) Place the following in both applications' <system.web> sections in their respective configuration files.
2) Update the .NET aspnet.config file located at "C:\Windows\Microsoft.NET\Framework\v4.0.30319\Aspnet.config" to change the following settings to the following values:<authentication mode="Windows" /><authorization><deny users="?" /></authorization>
3) In the code for the MVC app that wants to call the Web API 2 app, do the following:<legacyImpersonationPolicy enabled="false"/><alwaysFlowImpersonationPolicy enabled="true"/>
This last bit is necessary to ensure that the currently authenticated user in the MVC app gets correctly propagated to the Web API 2 app. In the service that's mentioned in the code above, you'll also need to do the following for interacting with Web API 2:WindowsIdentity identity = (WindowsIdentity) this.HttpContext.User.Identity;using (identity.Impersonate()){searchModel.SearchResults = this.webApiService.FindWebApiItems(searchModel.SearchCriteria);}
4) Use the following to connect to Web API 2:
HttpClientHandler handler = new HttpClientHandler
{
PreAuthenticate = true,
UseDefaultCredentials = true,
Credentials = CredentialCache.DefaultNetworkCredentials
};
using (HttpClient client = new HttpClient(handler))
{
string result = client.GetStringAsync(uriBuilder.Uri).GetAwaiter().GetResult();
IList<MyWebApiModel> webApiModels = JsonConvert.DeserializeObject<MyWebApiModel[]>(result);
return webApiModels;
}
You should now have working code to propagate Windows Authentication via services.
As a side note, apparently there's also a way to change the settings so that you don't have to modify the global aspnet.config file, you can do it on a per-AppPool basis. The technique is described here, though I've never tried it myself.
Labels:
API,
asp.net,
authentication,
impersonation,
mvc,
web,
webapi,
webapi2,
windows
Monday, April 27, 2015
A little gotcha with the Microsoft Task Parallel Library (TPL) Dataflow library
I've recently started using the Microsoft TPL Dataflow library in order to help improve the performance of some of our product's scheduled jobs. However, today, I ran into a little gotcha that's pretty important. When you're linking one block to another block, you must always ensure that the source block links to at least one target block that accepts its messages, otherwise it'll most likely deadlock!
Wednesday, April 08, 2015
Resolving "Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'" in IIS with WCF web services
If you get an error that looks like "Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'" in IIS while testing WCF web services, it most likely means that you haven't correctly specified the certificate for your service in IIS. If you're certain you've correctly specified the certificate, it most likely means that the IIS user doesn't have access to it, so you'll have to go in and manually grant permissions for it.
Removing a certificate binding from a port in Windows
As many people don't know, in Windows certificates can be bound to ports for securing content transferred over those ports. IIS happens to be particularly negatively affected by this if another program has a certificate bound to a port that you want to use, e.g. 443 for serving web pages.
Use the information at the following page to find the certificate binding and delete it :
https://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx
The short version:
Find the port: netsh http show sslcert | grep -C 5 443
This command will show all the SSL certificates that are bound to ports on your machine.
Delete the port: netsh http delete sslcert ipport=0.0.0.0:443
This should help deal with some of the more annoying (and less verbose) errors when doing things like trying to configure WCF services to use SSL.
Use the information at the following page to find the certificate binding and delete it :
https://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx
The short version:
Find the port: netsh http show sslcert | grep -C 5 443
This command will show all the SSL certificates that are bound to ports on your machine.
Delete the port: netsh http delete sslcert ipport=0.0.0.0:443
This should help deal with some of the more annoying (and less verbose) errors when doing things like trying to configure WCF services to use SSL.
Friday, March 20, 2015
Creating ChannelFactory instances that are configured with Custom credentials in WCF
Check this out: Setting Client Credentials
I just discovered this wonderful piece of information on how to properly create ChannelFactory instances in WCF for clients.
I just discovered this wonderful piece of information on how to properly create ChannelFactory instances in WCF for clients.
Sunday, March 15, 2015
Using Ninject in an IIS-hosted environment to create a WCF service instance that's in a shared assembly (i.e. not the web application assembly)
Recently I wanted to create several WCF services for some of our internal applications and host these services in multiple different servers. There'd be no difference in the code between the locations where they'd be hosted. In order to avoid code duplication, I wanted to make these WCF services common and then just host them in the IIS applications. However, it's not as simple as creating a new (shared) assembly, creating the WCF service in that assembly and then sharing it between the different services. When I tried this, I ran into the problem where even though I specified the service in the config section:
After adding the .svc file and using the path to it in the system.serviceModel configuration in the configuration file, everything worked perfectly.
<system.serviceModel>....
</system.serviceModel>
... it still wouldn't find my service and load it (I'm using Ninject as a dependency injection container for my WCF services). They key to getting the shared services to be found and loaded properly was to create a .svc file in each of the services where the WCF services were shared, and it looked something like this in each case:
<%@ ServiceHost Language="C#" Debug="false" Service="MyWcfSharedServices.MySharedWebService" Factory="Ninject.Extensions.Wcf.NinjectServiceHostFactory" %>
After adding the .svc file and using the path to it in the system.serviceModel configuration in the configuration file, everything worked perfectly.
Wednesday, January 28, 2015
Resolving "The agent process was stopped while the test was running" on a TFS build agent
I'm currently working on a project that uses TFS for automated builds. Recently, our builds started failing with the error "The agent process was stopped while the test was running" while running unit tests. It wouldn't always happen during the same test, though it did frequently.
How We Solved It
By some miracle, one of our developers decided to run our problematic tests with mstest on the command line on his local machine. As it turns out, this was great because it showed the stack traces for the loose threads on the command line. Turns out we had a lot more loose threads than just the one that was taking down our test agent on the build machine. The command the developer used was (similar to) the following:
mstest.exe /testcontainer:"C:\path\to\my\test.dll" /noisolation
After auditing all of the errors by rooting them out with command line runs of mstest, the solutions to our problems all boiled down to one thing:
*ALWAYS* wrap the contents of 'async void' methods with a try-catch block!
After inspecting the Event Viewer on the build machines, we found an error in the .NET Runtime that was taking down the build agent:
The exception that was being thrown on a ThreadPoolThread (always extremely bad, never good) was a MockException: our unit testing framework was throwing an exception that was taking down the build agent. But where?Application: QTAgent32_40.exeFramework Version: v4.0.30319Description: The process was terminated due to an unhandled exception.Exception Info: Moq.MockExceptionStack:at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__5(System.Object)at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()at System.Threading.ThreadPoolWorkQueue.Dispatch()at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
How We Solved It
By some miracle, one of our developers decided to run our problematic tests with mstest on the command line on his local machine. As it turns out, this was great because it showed the stack traces for the loose threads on the command line. Turns out we had a lot more loose threads than just the one that was taking down our test agent on the build machine. The command the developer used was (similar to) the following:
mstest.exe /testcontainer:"C:\path\to\my\test.dll" /noisolation
After auditing all of the errors by rooting them out with command line runs of mstest, the solutions to our problems all boiled down to one thing:
*ALWAYS* wrap the contents of 'async void' methods with a try-catch block!
Labels:
async,
async void,
catch,
debug,
debugging,
error,
error handling,
Exception,
mstest,
Task,
try,
void
Thursday, January 22, 2015
Implementing a WCF service on an Active Directory domain using a ServiceHost in a Windows Service with Windows authentication and a non-system user service account
So, as it turns out, when you want to implement a WCF service on an Active Directory domain using a ServiceHost in a Windows Service with Windows authentication and a non-system user service account, you have to jump through a few hoops for the configuration. I kept getting the error message "A call to SSPI failed. see inner exception". The inner exception is "The target principal name is incorrect.". Like many other people, I kept thinking it had to do with authentication of the *client*. As it turns out, like those other people, I was wrong. It was to do with *verification of the service account running the service*. This is presumably because the *service user is a domain user service account*. To get this scenario working, you have to specify the name of the service user account as the 'userPrincipalName' in the 'identity' element of the 'endpoint' element for your service, like so:
<configuration>Now you'll get proper connections and authentication via Windows.
<system.serviceModel>
<services>
<service name="MyProject.MyService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:12345/MyService"/>
</baseAddresses>
</host>
<endpoint name="MyServiceTcpEndpoint"
address=""
binding="netTcpBinding"
bindingConfiguration="MyServiceTcpBinding"
contract="MyProject.IMyService">
<identity>
<userPrincipalName value="MyDomainName\MyServiceUserName"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="MyServiceTcpBinding"
transferMode="Buffered"
maxReceivedMessageSize="65535">
<security mode="Transport">
<!-- Use Windows authentication to ensure that we at least have authentication if not encryption -->
<transport clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
Adding files generated at build time to your ClickOnce deployment
So, it used to be that I didn't really like ClickOnce deployment, but it turns out it has a lot of benefits:
- It provides infrastructure that makes it *exceedingly* easy for your average lay-user to upgrade. All they have to do is start the program as normal, and it automatically updates for them (when configured properly)
- It bundles everything up nicely and is easy to install and uninstall (if you're a real person. If you're a script, not so much)
I recently encountered the problem of including files generated at build time (e.g. transformed configuration files) in the ClickOnce deployment package. I found the following that I could include in my ClickOnce project's .csproj MSBuild file:
<Content Include="Path\To\My.file" Condition=" Exists('Path\To\My.file') ">The key part to getting it to work properly is that your build has to do a Build target first, then a Publish target (usually with two separate invocations of MSBuild via a build script, rather than building with a Solution). Fortunately, I was already set up for that, so getting the extra files included was a dream.
<Visible>false</Visible>
</Content>
Friday, January 16, 2015
Querying XML with namespaces in PowerShell
Apparently it's ridiculously easy to parse XML in PowerShell once you have the right objects set up:
In this example, I query a deployment configuration for attributes called "ServerHostName" within the XML namespace "http://www.mycompany.com/Some/Namespace/Deployment/DeploymentProfiles.xsd". This was exceptionally useful today when I quickly needed to figure out what servers my system was using and send that information to others to configure those servers.
[xml] $xml = Get-Content .\DeploymentProfile.PRODUCTION.xml
$ns = new-object Xml.XmlNamespaceManager $xml.NameTable
$ns.AddNamespace('dns', 'http://www.mycompany.com/Some/Namespace/Deployment/DeploymentProfiles.xsd')
$xml.SelectNodes("//@dns:ServerHostName", $ns) | ForEach-Object { $_.Value } | sort | Get-Unique
In this example, I query a deployment configuration for attributes called "ServerHostName" within the XML namespace "http://www.mycompany.com/Some/Namespace/Deployment/DeploymentProfiles.xsd". This was exceptionally useful today when I quickly needed to figure out what servers my system was using and send that information to others to configure those servers.
Labels:
namespace,
namespaces,
parsing,
powershell,
XML,
xpath,
xquery
Implementing Code Analysis with Team Foundation Server 2012 and later
I've been looking to get Code Analysis going on our team project at work. I found this helpful MSDN article.
Adding a computer to the local machine's list of PowerShell TrustedHosts
Retrieve the current list:
$curValue = (get-item wsman:\localhost\Client\TrustedHosts).value
Set the new list, appending the new host onto the old list:
set-item wsman:\localhost\Client\TrustedHosts -value "$curValue, Server01.Domain01.Fabrikam.com"
$curValue = (get-item wsman:\localhost\Client\TrustedHosts).value
Set the new list, appending the new host onto the old list:
set-item wsman:\localhost\Client\TrustedHosts -value "$curValue, Server01.Domain01.Fabrikam.com"
Tuesday, December 09, 2014
Things I've learned (forcibly) on 2014-12-09
- There are database systems that are actually implemented using the Julian (sort of) calendar. Why ?!
- It is perfectly legitimate to use script types other than type="text/javascript" in a
Saturday, December 06, 2014
Adding less to your MVC 5 project
I'm going to have to just redirect to this post, because I'm in a rush. Hopefully it's still around when you read this.
Note: In order to get this to work, I had my application pool running in 'Integrated Pipeline Mode' and IIS 8 didn't like that, so I had to get rid of the system.web/httpHandlers sections for the dotLess configuration.
Note: In order to get this to work, I had my application pool running in 'Integrated Pipeline Mode' and IIS 8 didn't like that, so I had to get rid of the system.web/httpHandlers sections for the dotLess configuration.
Subscribe to:
Posts (Atom)