April 9, 2010

Always Learning

Okay, this one is more about the great community of powershell users. I took a task upon myself after realizing user’s admin accounts were not part of the password synchronization process. The task was a good way to show the group how Powershell could be used. Being a huge fan of one-liners I sent some informative emails showing how easy it was to list user accounts and vital information such as whether it is disabled or the password expired. Its really not about that though. Based on the results it seemed logical to create a script to perform an audit, to include notification to users that their password would expire.

So, onto the point of this posting. I started working on the script, tested the logic for identifying password expiration now it was time to work on formatting the email message(s). I could have made it a simple one line email but considering the recipients I wanted it to be a little more informational.

II have experimented with Here-Strings but never really used them so here was a chance to put it to use…. Doh! its not working, research the problem a little and the problem was, in following proper indentation the string completion meant the whitespace, aka blank spaces, aka tabs, aka indentation was considered the first characters on the line. Wondering if maybe I was missing something I posted to the powershellcommunity.org. I have always been impressed with how quick and in-depth people work to help others on this site. Almost instantly it was confirmed that there was no workaround with here-strings but also a ton of recommendations for alternatives. Then some feedback in terms of sure indentation is great but for the sake of performance and compliance its no crime to break the format to use a true Powershell function versus going through more complicated methods… I hesitated and as you see in the full code listing below I tried two methods and stuck with breaking the “pretty” formatting of the code to use the Here-String. So as the following code shows $usrmsg breaks the indentation, it provides a concise way to build a body of text. I would also like to point out, using a Here-String also made it easier to pass to the function that actually sends the email. A here-string starts with @” and ends with “@, so below you can see it begin with $usrmg=@” so the string will be stored in a variable named $usrmg. Very simple combination of text lines with dynamic data (i.e. $msgdate will actually be the date when the script is executed) assembled and stored to be passed on, the result will be used as the body of the email message.

Done…. not so fast. With Powershell Version 2 sending an email is easy since they included a cmdlet for just that. Code was done, test worked but the actual send-mail message structure was just one very long line. I was using Bing to search for more help with a send-mail message parameter and came across a great blog posting by the group who developed Powershell. Again, very impressed that they not only wrote the language but do a great job of encouraging its use with some great examples and explanations. In reading they address the specific topic of send-mailmessage getting unwieldy and I just had to use it. Basically it stores all of the parameters in a hash table. The resulting code looks cleaner, easier to debug and an added benefit was I could code it out with two values for the email address the email would go to, so one it was ready to go all I need to do is remove the line that had To = my email address and then remove the comment (#) for the entry that used the dynamic value of the user’s email address for notification.

function usrnotification {
    param(
    [string]$varname ,
    [string]$usremail ,
    [string]$vartil 
    )

    Process {
    $msgdate=(get-date).tostring()
#    $msgbody=[System.String]::Format("{0}`r{1}`r{2}",
#    "Message generated: $msgdate from $env:COMPUTERNAME on $msgdate ",
#    "The password for your Windows Admin account $usersam is set to expire in $vartil days  ",
#    "Please verify and change your password."
#    )

# NOTE: Indentation ceased for message text creation
$usrmsgtxt=@"
Message generated: $msgdate from $env:COMPUTERNAME on $msgdate .
The password for your Windows Admin account $usersam is set to expire in $vartil days.  
Please verify and change your password.
"@ # End of message text assembly
    $bodytxt =  $usrmsg | out-string
$usrmsg = @{
        Subject = "Windows Admin Account Password Expiration"
        From = "sender@sendingdomain.com"
        # To = $usremail
        To = "recipient@todomain.com"
        smtpserver = "servername.yourdomain.com"
        Body = $bodytxt
    } # End of mailmessage parameters
    send-mailmessage @usrmsg


No revolutionary code, just a great experience with a great tool and even greater collection of people energized and collaborating for the good of the community.

April 2, 2010

Home Drive Mapping

So, this one is quick and was primarily meant to be a quick example when presenting on the Quest AD cmdlets.  The practical piece is to deal with something we experience when working remotely.  Typical scenario, user logs in locally to their laptop and then connect to the corporate network via VPN.  The home drive is not mapped because when the desktop state is loaded the VPN connection has not yet been made.  So the script showed how to add a little function to their powershell profile so they could simply run the function to make their connection.

function map-homedrive {
# simple function to map the drive based on your user info
$nw = New-Object -ComObject Wscript.Network
$nw.MapNetworkDrive((Get-QADUser $env:USERNAME).HomeDrive, (Get-QADUser$env:USERNAME).HomeDirectory)
}

The script simply  determines the current user name ($env:USERNAME), then reads the configured values for the corresponding Active directory object for the drive letter (Get-QADUser $env:USERNAME).HomeDrive and then the path (Get-QADUser $env:USERNAME).HomeDirectory , combining those two values to perform the drive mapping.  

March 24, 2010

Editing

Just thought I would post a quick note on my experience with the Powershell ISE.  In the past I have stated my preference for Powershell Plus as my Powershell script editor.  I am still a huge fan (well other than the new version seems to have an issue) of the product and in truth it is much more than a script editor. 

I also enjoy the PowerGUI editor but in general I do not use the PowerGUI suite, but when working on a script on a machine that isn’t my workstation, it was easy to install PowerGUI and have access to a decent editor.

With Powershell V2, the ISE was introduced.  I had used it briefly when giving an internal demonstration of Powershell to my colleagues.  I also must admit that I tried to customize it using the ISE specific profile but failed miserably and had given up on it.

A recent project had me developing a script on a server and considering latency it was just easier to RDP into the server and interact with the console.  With the ISE, I had access to an editor and a powershell command line interface without installing PowerGUI or any other software.  I am very impressed with the ISE for developing and debugging scripts.

That said, I still think Powershell Plus is a great tool set.  Any script editor that includes syntax highlighting is helpful and there are some other niceties such as automatically certificate signing scripts, snippets and code completion.  Where I still use Powershell Plus the most is the actual shell.  Whether working on a script or just simple day to day sell usage, I have found Iuse get-help much less since within the shell I can type a cmdlet or function and it knows about most if not all the parameters and includes links to help documents or searches.

Okay I have to get back to completing this script.

 

Technorati Tags: ,,,

March 15, 2010

Exploring

Yes, there are some duplicates from my other blog.  Sorry about that but I am experimenting with moving my blog.  The Go Daddy admin tools are just too tedious and with the constant wordpress updates, it just seems like time to explore an alternative.

First annoyance here is displaying code.  Since this is primarily powershell code oriented, that is a big gotcha so I will be experimenting to see if I can work around it.

Version 2 vs CTP3 – Test-connection

Interesting little discovery.  Working an a new Windows2008 R2 Server I started working with the test-connection cmdlet.  After testing some code I was ready to reduce some lines of code in some production scripts.  I quickly found out that test-connection <machine name> –quiet doesn’t work.  This would have helped because in current scripts there is an elaborate use of ping and reading the results which with Powershell V2 can be replaced with:

if (test-connection <machine name or ip> -quiet){ 
    # Some code here } 
Else {"Can not contact target machine"}

Well I will have to put this in the snippet library and put it into action when the full version 2 is available for XP.

March 9, 2010

Scripting On the Fly

Okay so this code is not the elegant sort of code you see on other sites, it is truly a SysAdmin writing functional code while performing Admin functions (hence why the output is handle on the command line and not inside of the script). 

The patch released this past week to address the critical security vulnerability in Internet Explorer prompted action.  On a Friday evening the call went out to get all servers patched this weekend.  Having gone through this before I also knew someone will be asking for a report of what servers have been patched.  In the past I would have written something in vbscript. 

First step was to verify the WMI portion, to include the segment that would indicate the patch had been installed.  Going back one step, there had been a similar critical patch and was never able to get the Powershell code (V1) to work and ended up going with vbscript.  After verifying the WMI with some test cases, I passed on how to use Powershell to check a server to include offering my colleagues a WMIC alternative.

This code was tweaked several times, as the weekend progressed.  The final step was to provide more feedback.  At first it would just report, could not connect, installed and not installed.  I knew people would raise the eyebrows with that information.  I knew that things such as test-connection failing could be for several reasons and leaving it at “could not connect” would make the report suspect.  Also, the WMI query returning no data could mean the patch wasn’t installed but it could be because of other reasons such as Access Denied or RPC issues.

So the process was, patch a server while the code was running, keep checking the output looking for anomalies.  Then with some extra time I started looking at how I could include the current error data to the output instead of the generalized result I was presenting at first.

Below is the code, caution any of you Powershell purists because it might not be pretty but seems to work. :-)

#requires -Version 2
<#
.SYNOPSIS
      Checks for install status of KB978207.
.DESCRIPTION
      Uses a source file of server names, makes sure the server can be connected to,
      then uses WMI to query the QFE information.  Looks for the instance in the QFE
      where HotFixID matches 978207.  
.EXAMPLE
      c:\temp\run.ps1 | out-file c:\temp\audwstats.txt
.LINK
      http://www.microsoft.com/technet/security/bulletin/ms10-002.mspx
#>
function chk-updt {
$curError=$Error.count    # establish current index of the error variable
$qfedat = gwmi win32_quickfixengineering -computer $server  |  where {$_.hotfixid -like "*978207*"}
      #if no data is returned the qfedat variable will be null
      if ($qfedat -eq $Null){
      # Since no data was returned check to see if that was because of an error, if so report the 
      # returned error message
          if ($Error.count -gt $curError){$server + ": " + $Error[0].Exception.Message}
          # No error generated by WMI query so consider the patch not installed
          else {"Patch not installed on " + $server}
       } # Finished processing Null $qfedat
      # An instance was returned therefore the patch has been installed 
      else {$qfedat.HotFixID + " Installed on " + $server}
}  # end chk-updt
 
# Format report header
get-date
"-----------------------------------------------------------------------"
$ErrorActionPreference="SilentlyContinue"       # Keep the screen chatter done and continue after error
$noconnect=0
$servers = gc c:\temp\full.txt                              # Define array of server names from text file
foreach ($server in $servers) {
      # verify the server can be connected, if it can call the chk-updt function
      if (Test-Connection $server -Count 1){chk-updt}
      # if the server can not be connected, report the specific reason for failure
      else { $server + ": " + $error[0].Exception.Message
          $noconnect=$noconnect +1 }
}
"-----------------------------------------------------------------------"
"Servers Checked:  " + $servers.count 
"Servers not reachable: " + $noconnect
get-date

 


 


 



August 18, 2009

One-Liners

As I mentioned in the previous post I have not written any formal scripts in some time.  I have focused on one-liners recently.  What is a one-liner you ask?  Powershell offers a great deal of tools that can be called up and used right from the Powershell command line. 

I think this is were Windows SysAdmins need to catch on to Powershell.  Scripts are great, especially for repetitive or large tasks but sometimes that is too cumbersome when you need to tackle a quick task or return information quickly. 

How does this apply?  We were recently asked to verify the specific version of Windows installed on a list of machines.  Yes, I had written a vbscript a few years ago to do that but wanted to approach it with Powershell.  Being lazy, I knew WMI would return the information so looking at my existing script I started with a simple wmi query: gwmi win32_operatingsystem, which returns several useful fields of data such as Build Number and Version.  For our use they wanted the publicly known name which I knew from previous of WMI was the Caption field so I was able to expand the one-liner to gwmi win32_operatingsystem | select caption so now I am accessing the same collection of information but now I am selecting just a specific field which is the Publicly known name of the operating system.

caption
-------
Microsoft Windows 7 Ultimate

So, you are sitting there and saying “I can just right click on the computer object and check properties”, but what if you wanted to check a remote computer, now you have to access that computer, with the one-liner I simply add the –computername parameter, as long as I have the necessary credentials I can return the same information from that computer.

Pretty easy right?  Pretty low key example though.  Now consider the ability to do the same type of operations with things like Active Directory and Exchange.  I have found it interesting that while on a conference call in the past, a request for information would be requested and the standard answer was a script, now in many cases a quick one-liner produces the basic information ASAP.  This is so much fun when someone estimates data and you can counter with the actual data, makes planning much more precise.