Wednesday, April 10, 2013

Forget Algebra, this is Powershell

Think back to basic algebra.  Some people struggle through it.  Some people are intrigued by it. A lot of people claim they have never used algebra in their entire adult life.  I have always worked an office job that included working in spreadsheets, so I know every time I'm constructing a formula in Excel I use algebra.  Programmers, and even people who use basic scripting, use techniques from algebra as well.

I remember learning some basic logical properties of equality waaaaay back in the beginning of algebra that seemed so obvious and logical that I didn't understand why we had to even write down that they were rules.

For example, the reflexic property of equality states that for any quantity a, a=a. Duh.  Why are we wasting time writing this down?  Ok, fine, for a test I guess I have to know that's the "reflexive" property.

How about the symmetric property of equality?  For any quantities a and b, if a=b then b=a. Again, duh. That's so obvious I can't believe someone wasted time writing it down.

Another example, the transitive property states that for any quantities a, b, and c, if a=b and b=c then a=c.  Even at the beginning stages of algebra, that seemed to me like it would be more useful.

For more refreshers on equality, there's a whole Wikipedia page on it.  http://en.wikipedia.org/wiki/Equality_(mathematics)


So what does this have to do with Powershell, anyways?   Today, I followed a Twitter link to a Powershell Mini-game on equality.  (http://beefycode.com/post/PowerShell-Mini-game-Equality.aspx). He presented the following:

Query: In PowerShell, when is the following statement true? Explain why.
      ( $a -eq $b ) -ne ( $b -eq $a )

Um, wait.  That can't ever be true, can it?  I mean, read the symmetric property of equality again.  For any quantities a and b, if a=b then b=a.  So, I learned way back in basic algebra that statement could *never* be true.  Or...could it?

Oh, dear.  There's an entire Powershell help file about all the comparison operators like -eq, -ne, -lt, etc.   In your shell, check out help about_comparison_operators or read the online version at http://technet.microsoft.com/en-us/library/hh847759.aspx

Guess what?  ($a -eq $b) can return MORE than just $true or $false.  It can return $true, $false, a value, or nothing at all. It means more than just 'equal to', it also means 'includes an identical value'.  So when comparing one value (a scalar) and a collection of values (an array), it's entirely possible that ($a -eq $b) is not the same as ($b -eq $a).   The help gives an example with strings. I'll provide another example with integers.

Let's say $array is an array containing the integers from 1 to 5 inclusive:
$array = 1..5
Now let's say $scalar is a scalar value equal to 4
$scalar = 4

Now watch this:
($array -eq $scalar) will return the identical value 4
($scalar -eq $array) will return $False

So guess what?  ($array -eq $scalar) -ne ($scalar -eq $array)

Monday, March 25, 2013


How about some code to get the new URLs that I've added to my favorites over the past 24 hours?


$NewFavorites = get-childitem $env:userprofile\favorites -recurse`
  -include *.url |
 where-object {($_.lastwritetime -ge (Get-date).adddays(-1))}
$urls = @()
Foreach ($NewFavorite in $NewFavorites) {
 $content = get-content $NewFavorite
 $url = ($content[1]).split("=")[1]
 $urls += $url
}
$urls


I need to test more - what if I've added 0 or 1 item to my favorites, does this still work?

UPDATE:
And then again, this guy's a genius :)
Courtesy of http://madprops.org/blog/list-your-favorites-in-powershell/
And modified to get just the 'new' URLS


gci $env:userprofile\favorites -rec -inc *.url |
  where-object {($_.lastwritetime -ge (Get-date).adddays(-1))}
    | ? {select-string -inp $_ -quiet "^URL=http"}
       | select @{Name="Name";`
          Expression={[IO.Path]::GetFileNameWithoutExtension($_.FullName)}},`
           @{Name="URL"; Expression={get-content $_ |`
               ? {$_ -match "^URL=http"} | % {$_.Substring(4)}}}


Hopefully my added line breaks still produce some code that runs.

Wednesday, September 12, 2012

PowerCLI - find hosts with no datastores

Someone had set up a test ESX environment with ~50 ESX hosts on various hardware with various configs.  I needed to identify any ESX host in the environment that did not have any persistent storage attached.  With this information, I could calculate the size of the NFS mount to request.

I set a variable $Loc that contains the location of the hosts.  I also wildcarded the name parameter because there was one host in that location that was not part of my environment.  After connecting to my VIServer, this command provided a list of the ESX hosts with no datastores attached.

Get-VMHost -Location $Loc -Name "*test*" | where-object { !@($_ |get-datastore).count} | sort-object name |select-object -property name

Need to know how many hosts are in that list?  Put that whole command in parentheses and add .count to the end of it:

(Get-VMHost -Location $Loc -Name "*test*" | where-object { !@($_ |get-datastore).count} | sort-object name |select-object -property name).count

Now, I know I need 8.5GB of storage for each host.  Just let PowerCLI do the math:

(Get-VMHost -Location $Loc -Name "*test*" | where-object { !@($_ |get-datastore).count} | sort-object name |select-object -property name).count * 8.5

So there we have it.  Technically, the second and third commands up there should also omit the 'sort-object' step, since the order of the objects is unimportant when all we're doing is counting how many there are.  But at the command prompt it's easier to use the up arrow and just tack on the ().count

Tuesday, August 28, 2012

Came across this code in thePowershell in Depth.  It might have solved my problem with getting usable computer names from AD, but I don't have an AD to test against right now :\

Get-Process –computerName (

Get-ADComputer –filter * -searchBase "ou=WebFarm,dc=company,dc=pr" |

Select-Object –expandProperty Name

)

Monday, August 13, 2012

Set RDP over SSL using Powershell.

Task:  Configure all Windows servers to use RDP over SSL to mitigate a man-in-the-middle vulnerability in Microsoft's RDP protocol.

Difficulty:  We do not run Microsoft certificate services. We self-sign certs from a Linux system and they are provided in .pfx format. Auto-enrollment is not an option, and I can't use Group Policy to assign a certificate because (from what I can tell) it requires Microsoft's certificate services.

In doing my research, I found a blog post that provided a wmi script that supposedly would do the configuration.  However, I was never able to get the script to work. (See Part II in http://blogs.msdn.com/b/rds/archive/2010/04/09/configuring-remote-desktop-certificates.aspx ).

So here is a Powershell way to do this.  This would be cool if it were a function and parameterized and provided error checking, but you get what you get.  Some things to point out - I had to generalize the path to the pfx file and the password, so I'm not completely sure the $Hashcode line works.  I had deployed this with the Certutil command in a batch file because I was rolling this out in phases instead of dumping everything in one script. Hopefully that works.  The "meat" of the script is everything that starts with $TSGeneralSettingRDP and that stuff all works.



$server = $env:computername
$PathtoPFX = "c:\temp\cert.pfx"
$PFXPass = "pfxpassword"
$Hashcode = (New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($PathtoPFX, $PFXPass)).Thumbprint
Certutil -p $PFXPass -importpfx $PathtoPFX
$TSGeneralSettingRDP =  get-wmiobject win32_tsgeneralsetting | where-object {$_.TerminalName -match "RDP-Tcp"}
$TSGeneralSettingRDP.SSLCertificateSHA1Hash = $Hashcode
$TSGeneralSettingRDP.SetEncryptionLevel(4)
$TSGeneralSettingRDP.SetSecurityLayer(2)
$TSGeneralSettingRDP.put()

Thursday, August 2, 2012

A Useful way to export a list of servers from AD

I was trying to come up with a list of all my servers from AD and write it to a text file.  Sounds like it should be a slam-dunk, right?  Get-ADComputer with some parameters and pipe it to out-file.  Look at the file in notepad, and it looks perfect.....and then I try to use that file as the input for another script.  Guess what?  the server names are all padded with spaces at the end of them, which causes all kinds of errors.


This works much better:
PS C:\temp> Get-ADComputer -Filter {OperatingSystem -Like "*server*"}  -Property Name | foreach {$_.Name.Trim()} |out-file filename.txt

Monday, April 30, 2012

More on string output

http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/29/weekend-scripter-handle-the-powershell-pipeline-and-use-the-console.aspx
I wish I had known this before the scripting games.  Tee-Output would have helped me.

Also, one of the comments is something that we talked about at the GMSC meeting, capturing verbose output.  After a bit of research, that looks like something that was built into Powershell 3.0. 

Neil's comment:
One of the things I have struggled with my scripts is capturing a verbose output.
For example:
copy-item c:\testing\test.pdf C:\testing2 -verbose
The verbose comes out to the screen beautifully but I can't seem to figure out how to capture it to a file.  I would like to capture to a file so that I can make sure the script was doing what I was thinking it was doing.
A bit of research brought me to this:
https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=297055&SiteID=99
It seems like it would be nice these "other" information/output channels
were assigned to streams so they could participate in stream redirection
like stderr (2>&1) e.g.:
...4 - verbose (write-verbose and cmdlet -verbose output)

And it looks like Microsoft has addressed this in Powershell 3.0.

# Scenario 3
# Redirect all output to a file (*>$filename)
$(Write-Output "Pipeline Output"; Write-Warning "Warning Output"; Write-Verbose "Verbose Output"; Write-Error "Error Output"; Write-Debug "Debug Output") *>$filename
# All output is now in the file
Get-Content $filename