Friday, October 20, 2017

Microsoft 70-487: Configure a web application for deployment

Exam Objectives

Switch from production/release mode to debug mode; use SetParameters to set up an IIS app pool; set permissions and passwords; enable and monitor ASP.NET App Suspend; configure WCF endpoints (including HTTPS protocol mapping), bindings, and behaviors; transform web.config by using XSLT (for example, across development, test, and production/release environments); configure Azure configuration settings


Quick Overview of Training Materials

Exam Ref 70-487 - Chapter 5.3
[MSDN] How to: Set Debug and Release Configurations
[MSDN] Understanding Build Configurations

[MSDN] Configuring Parameters for Web Package Deployment
[MSDN] Web Deploy Parameterization
[TechNet] Using declareParam and setParam
[Blog] MSDeploy with Declare and Set parameter files

[MSDN] Configuring Permissions for Team Build Deployment
[MSDN] Basic Security Practices for Web Applications
[MSDN] Best practices for deploying passwords
[Blog] Davidson Sousa - Setting folder permissions using Web Deploy
[Blog] Sayed Hashimi - Setting Folder Permissions on Web Publish
[Blog] Kevin Leetham - Modifying directory permissions with Web Deployment

[MSDN] Enable and monitor ASP.NET App Suspend on Windows Server 2012 R2
[MSDN] ASP.NET App Suspend – responsive shared .NET web hosting

[MSDN] How to: Transform Web.config When Deploying a Web Application Project
[MSDN] Web.config Transformation Syntax for Web Project Deployment
[GitHub] Xdt transform samples

[MSDN] Configure web apps in Azure App Service
[MSDN] Configure Azure cloud service roles with Visual Studio



Switch between release and debug mode


Visual Studio includes two build configurations by default: Release and Debug.  The difference basically boils down to two things:  debugging information (i.e. symbols), and optimization.  The debug configuration turns off compiler optimizations and outputs full debug info, whereas the Release configuration turn on optimization and limits debug symbol output to a pdb file:

Debug Mode



Release Mode


There is a drop down on the Visual Studio interface to switch between these two configurations (as well as any other additional configurations you may have defined).



It is important to note that in order to use the debug configuration, you must have debugging enabled in your project by setting the <system.web><compilation> element "debug" attribute to "true" (this is the default value).  The Exam Ref mentions this, but doesn't really explain the ramifications of turning it off.  If you try to run in Debug mode with the debug setting set to "false", Visual Studio will complain and ask you to fix it:





Use SetParameters to set up app pool


When you "Publish" a solution (as described in another post), one of the files that is created is the SetParameters.xml (it's prepended with the application name).  This file sets a number of parameters that are used by MSDeploy when you deploy the application, whether manually or by running the deploy.cmd batch file.

Some of the parameters in the SetParameters file are created automatically by Visual Studio when you publish the application (IIS Application Name and Connection Strings, for example).  You can also ensure that custom parameters are included in the file by creating a parameters.xml file in your application and defining the parameters you want set.  A simple example is included in the Configuring Parameters for Web Package Deployment article:


The resulting file, called WebApplcation2.SetParameters.xml, contains a couple of the automatically generated parameters, plus the custom one we defined in parameters.xml:


Another of the articles in the Microsoft documentation contrasts these web deployment parameters with Web.config transformations (which we'll look at below).  It makes the point that Web.config transformations are easier to set up, but require you to know ahead of time what will change and how, whereas parameters can be set at the time of deployment (it gives the example of a system admin entering production passwords that are not known to the developer).

The blog post on Twenty6 about the Declare and Set parameter files sheds a bit of light on what seems to be happening below the surface.  MsDeploy can take as arguments xml files that declare and set parameters.  The syntax for declaring parameters is what is used in the example above for the parameters.xml file in the demo solution, and the set parameters syntax is used by the SetParameters.xml file generated by the publishing operation.

That post references documentation I found on technet, though it strangely uses an IP address... weird.  The documentation states that declareParam is used during package creation, which corresponds to the publishing operation, while setParam is used at synchronization time.  One point that is important to grasp is that there are multiple types of parameter transformations that can be performed.  The <parameterEntry> element on the parameter declaration includes a "kind" attribute:

  • DeploymentObjectAttribute - parameterizes various deployment attributes.  Some examples that I've seen on GitHub [1][2], as well as the documentation examples, include the managed pipeline mode, the process model, username and password, the enabled protocols, and the application pool.
  • DestinationBinding - allows for manipulation of the http and https bindings.   I found one example on StackOverflow where this transformation fell short, so this might not be viable in a web farm scenario.
  • DestinationVirtualDirectory - the parameter specifies a physical path of a virtual directory. An example of this is included on the Twenty6 post.
  • ProviderPath - WebDeploy uses providers to access different resources, such as the registry, the acl, IIS settings, etc.  This parameter type allows you to vary these settings.  The Web Deploy Parameterization doc includes an example of using the iisapp provider to change the application path.
  • TextFile - This kind of parameter allows you to do string substitutions in a text file.  I found an example on StackOverflow that, interestingly enough, ran into issues with internationalization.
  • TextFilePosition - Parameterizes a text file based on line and column for a fixed length string.  I found a single example online, though this didn't seem to be production code...
  • XmlFile - Replaces content in an XmlFile.  The "match" attribute uses XPath to find the element to be substituted.  Samples abound of this kind of parameter: [1][2] are particularly good, and both include examples of TextFile transformations as well.

The "scope" attribute on the parameter entry is used differently depending on the parameter type.  For the text and xml file parameters, the scope defines the file system path to the files, whereas on the ProviderPath parameter type, the scope defines which provider is affected.



Set Permissions and Passwords


In many scenarios, it is appropriate to avoid giving developers (or, more importantly, hostile actors) more access to resources than necessary.  However, the difficultly lies in the fact that the application eventually does have to work in production, which may mean granting access to file systems, databases, apis, etc.  There are severals angles from which you can look at this problem, and since the objective is pretty vague, we'll just examine of few that I found.

The problem of setting file system permissions is addressed by the two blog posts by Sousa and Hashimi, respectively.  Their solution is to create a project specific .targets file that includes tasks for MSBuild to include setAcl items in the deployment package's source manifest.  This is the same manifest referenced by Web Deploy, where setAcl is a build in "provider".  The second step they take in the .targets file is to define a parameter that will allow the folder whose permissions are being modified to be set at deployment time.  This is essentially doing the same thing as we saw above with using SetParameter.  Another blogger, Kevin Leetham, takes a similar approach, but breaks down all the steps involved.  I cobbled together the Hashimi and Leetham examples, just to see what would happen:


<?xml version="1.0" encoding="utf-8"?>
<!--********************************************************************-->
<!-- Task Custom ACLs                                                   -->
<!-- This came from Leetham's blog                                       -->
<!--********************************************************************-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 
  <PropertyGroup>
    <!-- 
         Extends the AfterAddIisSettingAndFileContentsToSourceManifest 
         action do also set ACLs                                      
    -->
 
    <IncludeCustomACLs>TRUE</IncludeCustomACLs>
 
    <AfterAddIisSettingAndFileContentsToSourceManifest 
      Condition="'$(AfterAddIisSettingAndFileContentsToSourceManifest)'==''">
      $(AfterAddIisSettingAndFileContentsToSourceManifest);
      SetCustomACLs;
      DeclareCustomParameters;
    </AfterAddIisSettingAndFileContentsToSourceManifest>
  </PropertyGroup>
 
  <Target Name="SetCustomACLs" Condition="'$(IncludeCustomACLs)'=='TRUE'">
    <Message Text="Adding Custom ACls" />
    <ItemGroup>
      <!--
          Make sure the by default Networkservice/AppPoolIdentity 
          have write permission to the root                        
      -->
      <MsDeploySourceManifest Include="setAcl"
          Condition="$(IncludeSetAclProviderOnDestination)">
        <Path>$(_MSDeployDirPath_FullPath)</Path>
        <setAclAccess>Read,Write,Modify</setAclAccess>
        <setAclResourceType>Directory</setAclResourceType>
        <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
      </MsDeploySourceManifest>
    </ItemGroup>

    <!-- This came from Hashimi's blog -->
    <ItemGroup>
      <MsDeploySourceManifest Include="setAcl">
        <Path>$(_MSDeployDirPath_FullPath)\Elmah</Path>
        <setAclAccess>Read,Write</setAclAccess>
        <setAclResourceType>Directory</setAclResourceType>
        <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
      </MsDeploySourceManifest>
    </ItemGroup>

  </Target>

  <!-- This came from Hashimi's blog -->
  <Target Name="DeclareCustomParameters" 
    AfterTargets="AddIisAndContentDeclareParametersItems">
    <ItemGroup>
      <MsDeployDeclareParameters Include="ElmahSetAclParam">
        <Kind>ProviderPath</Kind>
        <Scope>setAcl</Scope>
        <Match>^$(_EscapeRegEx_MSDeployDirPath)\\Elmah$</Match>
        <Description>Add write permission to the Elmah folder.</Description>
        <DefaultValue>{$(_MsDeployParameterNameForContentPath)}/Elmah</DefaultValue>
        <Value>$(_DestinationContentPath)/Elmah</Value>
        <Tags>Hidden</Tags>
        <Priority>$(VsSetAclPriority)</Priority>
        <ExcludeFromSetParameter>False</ExcludeFromSetParameter>
      </MsDeployDeclareParameters>
    </ItemGroup>
  </Target>

</Project>

This was written to WebApplication2.wpp.targets in the root of the project, and when I published, there were two additional entries in the manifest, and an additional parameter in the SetParameters file.

For sensitive information like username/password, connection strings, and api keys, using the parameters.xml file to transform the config file is another approach, described in the How to: Use Web Deploy Parameters in a Web Deployment Package MSDN article.  Here they use an XmlFile parameter to modify the web.config file.  When the package is deployed through IIS Manager, the user is prompted to enter the values of these parameters.  This would allow the system administrator (or whoever is doing deployments) to enter sensitive data specific to an environment without needing to bake it into the code or try to keep it in a separate file.

Although, speaking of keeping secrets in another file, this is exactly one of the techniques suggested by the Best Practices for deploying passwords article.  In the web.config file (and presumably the app.config file...) there is a "file" attribute on the <appSettings> element that can point at another file that contains sensitive information.  If the file is not found, it is ignored.  Values in the external file will overwrite values that exist in the web.config file when they have the same key. Consider these two files:

web.config

  <appSettings file="Secrets.config">
    <add key="webpages:Version" value="3.0.0.0"/>
    <add key="webpages:Enabled" value="false"/>
    <add key="ClientValidationEnabled" value="true"/>
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
    <add key="Overwritten" value="false"/>
  </appSettings>


secrets.config

<appSettings>
  <!-- SendGrid-->
  <add key="mailAccount" value="My mail account." />
  <add key="mailPassword" value="My mail password." />
  <!-- Twilio-->
  <add key="TwilioSid" value="My Twilio SID." />
  <add key="TwilioToken" value="My Twilio Token." />
  <add key="TwilioFromPhone" value="+12065551234" />

  <add key="GoogClientID" value="1.apps.googleusercontent.com" />
  <add key="GoogClientSecret" value="My Google client secret." />

  <add key="Overwritten" value="true"/>
</appSettings>


The HomeController using ConfigurationManager to pull the keys for "Overwritten" and "GoogClientSecret", and the results show that the secrets.config file was in fact merged and used by the runtime, and "Overwritten" was clobbered by the external file:


public ActionResult About()
{
 var Overwritten = ConfigurationManager.AppSettings["Overwritten"];
 var GoogClientSecret = ConfigurationManager.AppSettings["GoogClientSecret"];
 ViewBag.Message = "Your application description page. Overwritten = " 
  + Overwritten + ", GoogClientSecret = " + GoogClientSecret;

 return View();
}




The "configSource" attribute offers similar functionality, but instead of merging the external config, all of the configuration must be moved into the external file.  If any elements are present in the config section which uses this attribute, the application will throw an exception on startup:




Enable and monitor ASP.NET App Suspend


App Suspend is a feature added to IIS in Windows Server 2012 R2 and the .NET Framework 4.5.1.  IIS App Pools can be configured with an Idle Time-out, which will shut a site down after a set amount of time.  This used to "terminate" the site entirely, so that when traffic hit the site again, it required a full cold startup to respond to new traffic.

The suspend feature is analogous to the "sleep" functionality of an operating system.  Instead of completely shutting down a website, it is suspended.  While suspended it consumes a small amount of memory, but is kept in a warm, "ready to go" state, and can be restarted significantly faster than a cold start up.  The Overview post on the MSDN blog shares some results from their testing, and includes a short video showing how to enable it. The "Enable and Monitor" post gives a short and sweet rundown of how to turn it on, and shows where the logs go.

Enabling it is pretty simple.  In IIS Manager, open up the "Application Pools" screen, go to the advanced settings for the application pool you want to configure, and under the "Processing Model" section, set the "IIS Idle Time-out Action" to Suspend instead of Terminate.  Easy as pie.





Monitoring App Suspend is accomplished with the event logs.  When an application is suspended, an event is triggered with id 2310.  In trying to spin up a website template to grab a screenshot of what the event looks like, I was reminded why I hate dealing with IIS... Had to go back into Server Manger and install a bunch of features that are required for a basic MVC app to run (thank you StackOverflow [1][2]).  Sometimes I feel like the whole fifth part of this test is just trying to trick me into learning IIS haha.  Anyway, after much toil, here is what the event looks like:




Configure WCF Endpoints, Bindings, Behaviors


The Exam Ref skips this entirely, and this is a rare occasion when I find such an omission totally fogivable.  This topic is absolutely beat to death in the WCF posts.  In particular, my post Configure WCF Services by using configuration settings covers this topic pretty thoroughly, and my post on Hosting a WCF Service covers configuration for IIS... so I won't repeat all that here. 



Transform Web.config using XSLT


The web config file can be transformed during the publication phase by using a transformation file.  The documentation on MSDN lays the groundwork for this functionality (and honestly I'm mostly just paraphrasing).  In the default MVC template, the transformations are placed in Web.Debug.config and Web.Release.config for each of the two default build configurations.  In these file(which like Web.config are XML files), the schema is very similar to the config file itself, but it also includes a namespace declaration for the XML-Document-Transform namespace.

Out of the box, almost all of the sample transforms in these files are commented out (the one exception being the transform to remove the "debug" attribute from the Release build).  These examples are pretty limited, though it wasn't hard to find a much more substantial list of examples with a quick google search.



There are two attributes from the xdt namespace that are used in the transformation: the Locator attribute, and the Transform attribute.  The locator attribute specifies how the element to be changed will be found, and the transform element specifies what is done with the element once we have it.

There are three locator types:

  • Condition - an XPath expression that is evaluated relative to the current element.  The example in the documentation looks for either of two attribute values: Condition(@name='oldname' or @providerName='oldprovider')
  • Match - given a comma separated list of attribute names, will apply the transformation on elements whose attribute values match all of the attribute values.
  • XPath - similar to the Condition locator, except the XPath expression is absolute



The transform types are pretty self explanatory:
  • Replace
  • Insert
  • InsertBefore
  • InsertAfter
  • Remove
  • RemoveAll
  • RemoveAttributes
  • SetAttributes

Because the GitHub samples are geared toward app.config settings for IIS (though still very good starting point for web app transformations), I decided to look around for another set of examples targeted specifically at web.config, and managed to find an old post on the asp.net blog: Common Web.config transformations with Visual Studio 2010.

One interesting point made both in Scott's blog and in a relevant StackOverflow question was the fact that when you want to replace an entire section of the config file, you don't have to specify a Locator.  You can just specify a transform of "Replace" and the contents of the development web.config will be completely replaced by those in the transform file.  He also demonstrates how to use the Match locator to replace an individual app setting value.  Another blog demonstrates something similar to replace assembly binding redirects. 

A common use case seems to be replacing connection strings and environment specific app settings.  It would also be a useful technique for manipulating WCF settings (particularly "address" settings to switch between dev, test, and prod endpoints).  Just keep in mind that transforms are only applied when the application is published, not when it is built.  Running a build in Release or Debug mode will still only give you the base web.config file.

Of course, it is worth noting that these transformations actually have nothing to do with XSLT, which is explained at the end of my XML post



Configure Azure Settings


Azure Web Apps offer functionality similar to that discussed in the "Set Permissions and Passwords" section, with App Settings and Connection Strings configurable from within the portal.  These configured values will override the values in the web.config (the documentation also covers other interesting settings that can be manipulated here):



For Azure Cloud Services, there are several files that define and configure the service, both locally and on Azure (referencing the documentation here).  The ServiceDefinition.csdef file defines the runtime attributes of the service, such as vmsize and endpoint bindings.  The other file is the ServiceConfiguration.cscfg file, which determines how many instances of a service will be run and sets the values of settings for the role.  By default two versions of the service config file are created when you create a new Cloud Service project:  ServiceConfiguration.Cloud.cscfg and ServiceConfiguration.Local.cscfg, which define the settings for the Azure deployed app and the local development copy running in the emulator, respectively.

Settings in these files can be changed through several interfaces in Visual Studio.  Right-clicking on the Role and selecting properties will allow one to change the instance count (stored as config) and the vm size (stored in the definition).  If you are not running the full emulator, then Visual Studio will complain if you try to set the Local configuration higher than 1.  This is also where you can configure additional service configurations besides Cloud and Local.




The one setting that does not appear configurable from the GUI interface is the <OsImage> setting, which is part of the ServiceConfiguration file.  I'm guessing that this is because this element was used to configure VM Roles, which are long dead in Cloud Services

Besides the <Role> element inside the Service Configuration, you can specify a Network Configuration (kudos to the Exam Ref for once for pointing this out).  While the Exam Ref only mentions this in passing, I found an interesting blog article that explains it more thoroughly: Deploying a Cloud Service to a VNet.  He doesn't demonstrate creating the virtual network (presumably this is done an many other tutorials), but he does show how simple it is to roll a web application onto an existing Azure virtual network, configure address space and subnets, blah blah blah.



No comments:

Post a Comment