My Powershell Surprise Of The Day: An Array’s -eq Operator

I’m always discovering stuff in Powershell.  I know I’m not a guru but I don’t think of myself as a novice either—and yet I keep coming across something that makes me think, “how could I have missed something so basic?”  Today it was the –eq operator for an array.

Read the rest of this entry »

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!

What Version Of Powershell Is On This Machine? Ask PSVersionTable

I didn’t know about this built-in variable until recently.  $PSVersionTable is new with Powershell 2.0.  (Thus you can tell if you have v2 installed by seeing if this variable exists.)

 

image

 

It’s a hashtable containing versions of various parts of your system.   For example, here’s what I see on my machine:

PS c:\temp> $PSVersionTable

image

Each value is actually an object of type System.Version. 

image

 

So, for example, next time Microsoft updates Powershell and you want to see if you have the update, you could type $PSVersionTable.PSVersion to find out.

I won’t use this information every day, but I’ve wanted it a few times, and now I finally know how to get at it.

I haven’t looked into the other values…I’m disappointed that the CLRVersion is showing as 2.0.  This was run on a computer with the Framework 4.0 installed and I was hoping to see it.

Rare PowerGUI 2.2 debugger problem

After upgrading to PowerGUI 2.2, which I use for developing Powershell scripts, I encountered a problem with the new debugger. I got it fixed, and am delighted with this version of PowerGUI.

But then a couple months later I fired up a VM that I hadn’t used for a while. I tried to debug a script and–BAM!–there was the error again. Then I had to go searching for the solution again.

So here’s my reminder, which may also help those rare people who have this problem.

Breakpoints don’t work at all if debugging a script contained in a directory that has certain special characters in its name. This is related to the same Powershell issue I blogged about at:

https://surroundingthecode.wordpress.com/2010/04/17/powershell-file-names-special-characters-frustration-eventually/

The solution is to simply put the script into a different directory, one with no unusual characters in the directory name.

Read more here: http://poshoholic.com/2010/10/21/powergui-pro-and-powergui-2-2-are-now-available/

Powershell + File names + Special Characters <> Frustration (Eventually)

Have you ever tried to work in Powershell with a file or directory name that has unusual charaters in it?  When I was new to Powershell I stumbled across this with the standard names my software-development group used for certain directories—they contained square brackets: [ and ].

If you haven’t already come across this, go ahead and try working with such a directory in Powershell.  Use Windows Explorer to create a directory named [PowershellTest].  Then, in Powershell, try to Set-Location (or CD) into your new directory.  No good.  You can try putting quotes around it too, but it won’t help.

image

Wasn’t that fun?  Frustrated yet?

Have no fear; there’s an answer.  Internally, Windows is still keeping track of the short file names in the “old fashioned” 8.3 format.  It seems that Powershell is using these internal names.

There are several good ways to get around the problem. 

  • Never use such names!  But that’s not always an option.

  • Use the short 8.3 name.  There’s a great article about this at:  http://support.microsoft.com/kb/142982.  Note that in this article there’s one thing not made clear.  It’s in the first bullet point, about stripping certain characters when Windows generates the short name.  It does NOT delete them; rather, it changes them to underscores.  So in my case (but it won’t ALWAYS be the same on your machine), the directory name translates into _POWER~1.  And this name works fine!

image

 

image

 

  • Use the –literalpath argument, available on some (but unfortunately not all) commands that work with file names.

image

  • Download Powershell Community Extensions from http://pscx.codeplex.com/Wikipage, and use the Get-ShortPath cmdlet.  Note that you’ll still need to use –literalpath with this cmdlet, but once you have the short name you can use it for any other operation you want.

usepscx

 

Hopefully one of these options will work for you!