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
I really needed this information on Parameter substitution last week when I was coding a disk space check script. 

http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/28/weekend-scripter-learn-how-to-handle-string-output-from-within-powershell.aspx

I think I'll rewrite that script!

Monday, March 19, 2012

All logged in for the International Powershell Users Group day!  I know I'm going to learn some great stuff.  Pretty exciting to be able to spend the time to do this at night.

Thursday, March 15, 2012

http://www.powershelldownunder.com has posted some cool videos on the topics in the study guide for the 2012 Scripting Games. I love the Aussie accent and appreciate the tips.

Wednesday, March 14, 2012

Meet the Judges - 2012 Scripting Games

The judges for the 2012 Scripting Games have been announced.  http://blogs.technet.com/b/heyscriptingguy/archive/2012/03/09/announcing-the-powershell-judges-for-the-2012-scripting-games.aspx

Is 3 years too aggressive to think I'd like to be at that level?  I don't know, I was just explaining to my daughter last night that "You have to dream big!"  She's a senior in high school and thinking pre-med.  Time for a taste of my own medicine, right?

Sunday, February 26, 2012

Logic outline for rename server script

Via Jeff Hicks on Twitter (https://www.twitter.com/jeffhicks), I found this post on renaming a computer using Powershell

http://newdelhipowershellusergroup.blogspot.in/2012/02/set-computer-name-using-powershell.html

I need to compare this on Monday to my work-in-progress script.  This script above is probably the same as what I had found, but lacks the error correction I am still working on.  What if someone enters the name '&laptop #1'?  Plus, I want to account for someone entering a FQDN or just the hostname.  Some ideas I was working on:

1. Check if it's a fully qualified domain name (regular expression check for hostname.something.something)
2. Quick check the hostname is valid for Windows
3. Quick check the hostname meets our server naming standards
4. Check if the DNS suffix meets our naming standards
5. Show the hostname and DNS suffix and prompt for confirmation before renaming the server

Another idea:
Require the hostname but make the DNS suffix optional, defaulting to our 'normal' DNS suffix. Similar to this:

Rename-Server -newname ServerA

would result in this type of output from the script:
Assuming default DNS suffix of mydomain.local. Please confirm is ServerA the correct hostname?
[Y] [N] [?]

But Rename-Server -newname ServerA -dnssuffix myotherdomain.local would also work.
Please confirm ServerA is the correct hostname and myotherdomain.local is the correct DNS suffix.
[Y] [N] [?]

Lots of high standards here for this script, I must remember the adage 'done is better than perfect' and have a working script that we can start using soon.  I could probably spend 8 hours perfecting the regex for the Quick check if hte hostname is valid for Windows.

Friday, February 24, 2012

I was interrupted in my script that I was working on yesterday, so maybe I'll get back to that this weekend.

The interruption today was to enumerate all of the servers in a particular OU that is going to have a Group Policy change coming up. We need to document all of our changes in a Change Management application, and one of the required fields is 'Affected Systems'.  I figured the easiest way to get a text list of the servers affected would be to run a Powershell script, and it took all of about 30 seconds to find something that would work.

http://social.technet.microsoft.com/Forums/en-US/winserverManagement/thread/a0cebabf-3fc9-49b0-be55-5e4ff3232b7b/

The code I used wasn't from the correct answer, but from this guy's answer:


$strComputers = @()
$ou = [ADSI]"http://www.blogger.com/ EB,DC=EB-MILLIMAN,DC=COM"
foreach ($child in $ou.psbase.Children) {
 if ($child.ObjectCategory -like '*computer*') { $strComputers += $child.Name }
}


This code puts the Computers into the string $strComputers, which in turn can be called...

I simply modified the code to reflect my own AD structure and then did a write-host $strComputers.

P.S.  My first professional job out of college was as a consultant with Milliman & Robertson (now Milliman, Inc.).  Whoever posted the code should be advised not to give away too much information about their internal AD structure on the Internet.


Update:  I've found that using variable names with Hungarian Notation ($strComputers) is not a best practice for Powershell.  http://www.powershellcommunity.org/Wikis/BestPractices/tabid/79/topic/Naming/Default.aspx

 

Thursday, February 23, 2012

A girl's gotta have choices!

*yawn* It's bedtime but I had to jot something down tonight. I know when I read this tomorrow I'm going to be embarassed by the lack of a cohesive thought process here.

I've been working on a script that will require the user to choose something.  Think this is outdated? http://blogs.technet.com/b/jamesone/archive/2009/06/24/how-to-get-user-input-more-nicely-in-powershell.aspx . 

I think what I'm looking for is more along these lines:
http://technet.microsoft.com/en-us/library/ff730939.aspx

More work on this tomorrow.  I have a lunch presentation to go to,  so no scripting over lunch again (that's three times this week, no wonder I'm behind on my studying!). The lunch presentation is about our industry and competitors, though, so it is good for learning the big picture.

Thursday, February 16, 2012

What process is listening on a port on a remote server?

Today's inconvenience involved wanting to query a list of servers to find out what process was listening on a particular port. I'd like to be able to use a txt file or csv as input. 

serverA,443
serverB,8888
serverC,11008

So, I need to figure out how to find out the full path to the process that's listening on port 443 on serverA, port 8888 on serverB, etc. 

I didn't write a script to do so because I didn't have time.  But I am putting the question out here because someday I should be able to script this.  My quick search online revealed many simple scripts that I could cut and paste on a local server, but I want something that will query a remote server (difficulty: I don't have Powershell remoting enabled, so it would have to run on my workstation and remotely query the servers).

Wednesday, February 15, 2012

Needed to quickly check disk space on a bunch of servers today and I found this one-liner via Bartvdw (http://bartvdw.wordpress.com/2008/06/19/powershell-how-to-retrieve-disk-size-free-disk-space-for-a-list-of-computers-input-file/)


Get-WMIObject Win32_LogicalDisk -filter "DriveType=3" -computer (Get-Content c:\scripts\computers.txt) | Select SystemName,DeviceID,VolumeName,@{Name="size(GB)";Expression={"{0:N1}" -f($_.size/1gb)}},@{Name="freespace(GB)";Expression={"{0:N1}" -f($_.freespace/1gb)}} | Out-GridView

Perfect? No.  But it did the trick and it did it quickly.  And it was a nice reminder to me that if you cut and paste a script from the web, sometimes you need to find and replace all the quotation marks because blog sites like to turn them into pretty quotes instead of the plain straight quote marks that Powershell needs. 

If you get this error:

Unexpected token ':N1' in expression or statement.
At :line:1 char:179
+ Get-WMIObject Win32_LogicalDisk -filter "DriveType=3″ -computer (Get-Content c:\scripts\computers.txt) | Select SystemName,DeviceID,VolumeName,@{Name="size(GB)";Expression={"{0:N1} <<<< " -f($_.size/1gb)}},@{Name="freespace(GB)";Expression={"{0:N1}" -f($_.freespace/1gb)}} | Out-GridView

Then check your quotes.