Scripting IIS7 Application Pool Configuration in Powershell

I knew that scripting the configuration of a whole environment seemed the right thing to do.  But when we were building up our testing and production environments I was new to some of the technology and we were in a hurry and…you know the rest.  I only did the easy parts.  But now we’re looking at some changes that will require a ground-up reinstall/configuration of our web and application servers.  This time around I’m going to script much more, I hope, subject to time constraints I can’t control.  Today’s topic:  IIS7 application pools.

Incidentally, for a good argument for scripting configuration, see the book I blogged about earlier, which I’m still reading.

We take most of the defaults for IIS7 app pools, overriding a few.  I plan to use Powershell code similar to this: 

   1: if (@(Get-PSSnapin | Where-Object {$_.Name -eq "WebAdministration"}).Count -eq 0)

   2: {

   3:     Add-PSSnapin WebAdministration

   4: }

   5:  

   6: $cred = Get-Credential "MYDOMAIN\THEuserACCOUNTforTHEappPool"

   7:  

   8: $userName = $cred.UserName

   9: $password = $cred.GetNetworkCredential().Password

  10:  

  11: if (Test-Path IIS:\AppPools\MyTestAppPool)

  12: {

  13:     Remove-Item IIS:\AppPools\MyTestAppPool -Force -Recurse

  14: }

  15:  

  16: $myNewPool = New-Item IIS:\AppPools\MyTestAppPool

  17:  

  18: $myNewPool.processModel.userName = $userName

  19: $myNewPool.processModel.password = $password

  20: $myNewPool.processModel.identityType = "SpecificUser"

  21: $myNewPool.processModel.idleTimeout = [TimeSpan] "0.00:00:00"

  22: $myNewPool.managedRuntimeVersion = "4.0"   # or 2.0

  23: $myNewPool.recycling.periodicRestart.time = [TimeSpan] "00:00:00"

  24:  

  25: $myNewPool | Set-Item

 

A few things to mention:

  • I have verified the correct properties in IIS Manager, and it starts fine, but nothing is using it yet.  I will update this if necessary.
  • You’ll see in the code that I’m setting the pool’s identity to a domain user.  If you do this, use the minimum possible permissions.  Consider using Application Pool Identities instead for greater security.  In fact, for the web servers (not application servers) I plan to experiment with this.  I was not familiar with it until yesterday.
  • One of the links I read seemed to indicate that using this method would leave the password in plain text in the IIS config file.  I tested it, and no, the password is encrypted as it should be.
  • Also note that I’m prompting for the credentials to use for the identity.  This keeps passwords out of the script.  In real use, the user name will vary between environments so I’ll need to pull this information from somewhere.

As is often the case I had to poke around a bit to find the information I needed.  Here are some useful links.

http://learn.iis.net/page.aspx/434/powershell-snap-in-making-simple-configuration-changes-to-web-sites-and-application-pools/

http://msdn.microsoft.com/en-us/library/7w2sway1.aspx

http://www.iis.net/ConfigReference/system.applicationHost/applicationPools/add/recycling/periodicRestart

Advertisements

Have You Forgotten A Critical Attribute Of Your Development Project?

WHITESPACE_HERE

Forget about what customers want—or at least, what they say they want—for a minute.

People with software development experience will want to talk about non-functional attributes, such as performance, scalability, capacity, usability, flexibility, maintainability, etc.  They may not be familiar with Tom Gilb’s "Principles of Software Engineering Management,” a favorite book of mine (see Amazon.com), but they would probably agree with his statement on the importance of managing critical, non-functional attributes:

WHITESPACE_HERE

The Achilles’ Heel Principle:

Projects which fail to specify their goals clearly, and fail to exercise control over even one single critical attribute, can expect project failure to be caused by that attribute.

WHITESPACE_HERE

Software architects often struggle to convince other stakeholders that strict attention to non-functional attributes is vital to project success.  But even architects and senior developers often miss one of the critical attributes.  In fact their regard for it sometimes resembles the sales team’s regard for something as meaningless (to them) as maintainability.

The neglected nonfunctional requirement? 

WHITESPACE_HERE

Operability:  i.e., the ability to actually support the software after it has been launched.

WHITESPACE_HERE

To consider whether operability is being overlooked on your project, think about your answer some questions—there are more, but here’s a starter:

  • Are operations specialists (network and database administrators, customer support teams, any others who will be running the show when it’s in the hands of its users) involved throughout the entire development process?
  • Do they gain frequent experience with the software by deploying it into production-like environments?
  • Are you delivering functionality to customers in small increments? (Just as “big bang” doesn’t work well for customers, it’s usually a disaster for operations.)
  • Do developers need permission in the production environment to see data, logs, etc., on a regular basis (or, heaven forbid, to “fix” data or configurations)?
  • Is deployment to the production a nail-biting experience?
  • Does customer support know how to use the software?
  • If customers need training, has it been done, and using what materials?  Has the customer support team (and the development team) gotten feedback feedback from these sessions?

WHITESPACE_HERE

Like most of the other nonfunctional requirements, operability provides no "business value" to the marketing team.

WHITESPACE_HERE

Until you have actual customers, that is.

Then we find that everyone wants operability.

TFS Source Code Permissions Affect Branches

WHITESPACE_HERE

Today I solved a pesky problem with Team Foundation Server:  sometimes, some developers (but not others) could check into a branch even though all permissions had been denied.

My group does all development in the source code trunk, then branches at the end of any iteration that we intend to take up to our production environment. After early bug fix work on the branch we lock the branch down. Thereafter, developers have to ask for permission to check into the branch. This ensures that we know exactly what is being checked in, and when.  And we can decide, on a case-by-case basis, whether to allow it–no one wants a surprise going out to production.

WHITESPACE_HERE

So why could developers sometimes check into the branch even though all permissions had been denied?

WHITESPACE_HERE

As the TFS administrator (for the first time), I had beat my against this without ever getting anywhere. And recently I figured out the problem.

The key was to realize that the problem was described inaccurately.

Incorrect definition: sometimes a developer could get into a branch when I had (or thought I had) denied it.

Correct definition: that developer could ALWAYS get into a certain part of the branch, but not into other parts. Other developers might be denied in some areas but not others, too, but not the same parts as the first developer. Since all developers may sometimes work in any part of the branch the end result seemed somewhat random but that was not the case.

WHITESPACE_HERE

How does it happen?

WHITESPACE_HERE

Our source code tree is rather cluttered, and when I cut the branch intended for production, I end up actually branching several separate parts of the trunk, based on a common label, into a new, single destination root for the branch. I administer permissions to the branch at the root of the branch.

The problem: some subtrees in the trunk had explicit permissions granted to them rather than inheriting from higher up the source tree. These explicit permissions carried over into the branch, overriding the permissions I set at the root.

Very simple, once I understood it.

The solution was to keep an eye on where permissions were explicitly granted in the trunk, and remove those permissions in the branch so that all permissions get inherited from the root of the branch.  It would also be helpful to see where I can eliminate those explicit permissions and allow inheritance to do its work.

This fits the needs for my group, based on our local needs and the structure of our source code (which I inherited and do not like but am probably stuck with for a long time). It may not fit yours–but understanding that explicit permissions in a subtree of the trunk carry over into a branch may help you fix your own problems someday.

Credentials vs. WindowsIdentity in Powershell

I’ve been implementing some automated deployment processes using PS-Remoting, which means that I’ve also been learning about some security issues. This post is about how I solved a problem, and what I learned along the way.

WHITE SPACE

Background

My Windows login is in domain X, but the machine I run the deployment process from, and the machines being deployed to, are in domain Y. There’s a one-way trust set up, so that I have no troubles logging into the machines in the Y domain, belonging to the local administrator group on each machine, pushing files, etc., using my domain X login–but from a domain Y machine I cannot see or do anything with machines in the X domain. All as it’s meant to be.

WHITE SPACE

Problem

The trouble was that remoting didn’t work in domain Y unless I used a domain Y login. We have multiple test environments, with the lower ones (including the lowest: my box) all in domain X, where everything worked fine. We finally got up to the first environment which completely mirrors the security configuration of production, and BOOM! I couldn’t get a remote connection to anything.

WHITE SPACE

Resolution

OK, first off: HOORAY for having a test environment that mirrors production security! I think that this is always worth doing, and the latest issue is an excellent example. It would not have been fun to catch this on production deploy night.

But now: how did I solve it?

I have an "infrastructure" team at my organization that takes care of the network infrastructure. Working with them, we had a hard time figuring out what to do to resolve the remoting problem. We tried three things:

  1. Figure out why the trust that is setup doesn’t support what we’re trying to do with remoting. We struck out after hours of research. Maybe we’ll get an answer some day.
  2. Short term, we created a special login in the Y domain. I logged in using that Y login, then was able to run the deployment process successfully, remoting to different machines as needed. So this worked, but was not something we wanted to support long term. From a network security standpoint, I am domainX\username, and that’s the only way I should be identified.
  3. A combination of two things gave us a satisfactory long-term solution:
    1. From the domain Y server where we wanted to initiate remoting, we added the target servers to the list of TrustedHosts: Set-Item wsman:\localhost\client\trustedhosts *.domainY -Force. (Note: “*.domainY” means “all machines in the domainY domain”. For this to work, you then need to specify the full domain name of machinename.domainY when creating a remote session.)
    2. We prompt for the deployer’s credentials using Get-Credential, then explicitly pass the credentials, using the -credentials parameter, whenever setting up a remoting session.

Option three worked for us, although we did notice that the authentication mechanism of the remote session used NTLM. We were unable to make it use Kerberos. I’ll bet that if we could figure that out, we wouldn’t have to use TrustedHosts at all.

WHITE SPACE

What I l Learned

Incidentally, I used [Security.Principal.WindowsIdentity]::GetCurrent() to find out about the type of authentication being used. Try this out yourself:

clip_image001

So then the problem became how to get the credentials in the first place. At first I couldn’t figure out why you can’t just automatically (no prompting) get a credentials object for the current user. I’m new to a lot of this and was pretty ignorant about it. But now I understand some of it.

A System.Security.Principal.WindowsIdentity object, which is what you get from a call to [Security.Principal.WindowsIdentity]::GetCurrent(), gives you information about your authenticated identity on the network. As such, we would hope that it’s highly secure. It doesn’t even contain your password.

However, a System.Management.Automation.PSCredential object, which is what you get from a call to Get-Credential, is barely more than a simple data structure. You can enter any kind of user ID and password–no authentication is done when Get-Credential is called. The PSCredential object has a method called GetNetworkCredential(). You might think from its name that it gives you a WindowsIdentity object, but no, it simply breaks your user name into separate Domain and UserName strings and hands you the credential password in clear text. Try it out with a dummy login and password–it works fine. No authentication is done, and although you’ll see a secure string for the password in the PSCredential object, you can see it in clear text by calling GetNetworkCredentials().

Clearly, the intended use of WindowsIdentity and PSCredential objects are very different. One contains secure information about a live, authenticated user on the network. The other is an object that is secure enough to pass over a network connection (since the password is a secure string), and which will be USED LATER to authenticate someone at the time a remote session is created.

Once I got that straight I could easily see why there’s no support for converting from one to the other.

I really do have to prompt for credentials in my deployment process, then pass those credentials when opening a remote session, in order to make remoting work in a mixed-domain environment. I wish I didn’t.  But at least I understand the difference between the two objects!

WHITE SPACE

And remoting is working fine!