Thursday, May 15, 2008

New laptop

I'm getting a new laptop at work on Monday. Started a list of the things to do right after I get it. In the order they hit the electronic page (but not the order they'll happen -- more on that in a sec):
  • Create utils and docs folders
  • docs should be a junction of c:\documents and settings\tss04\my documents (powershell New-Junction c:\docs 'C:\Documents and Settings\tss04\My Documents'
  • bring over My documents and docs folder contents (currently separate directories -- fixing that w/ the junction)
  • bring over oreilly folder (put in docs?)
  • install ant
  • install perl
  • install filezilla
  • install firefox
  • install powershell and power shell community extensions
  • install vim, configure _vimrc, vimrc_example.vim, mswin.vim, john.vim, filetype.vim
  • bring over utils
  • selectively bring over d:\ftp
  • bring over d:\powershell
  • bring over notes journal, archive
So the obvious out of order one is that I'm using PowerShell to configure my docs directory as a junction of My Documents before I've installed PowerShell... Reading the list, I know I've missed cygwin and all of my firefox add-ons, bookmarks, profile, etc. Oh, and VMWare -- that's going on this time. Maybe 7-zip. Maybe...

What's the first thing you install on a new machine? What are you going to leave off when you replace your current box?

Monday, May 5, 2008

Powershell template

I'm working on a template for all powershell scripts in our environment. Basically something everyone can take as a starting point for their scripts, that will guarantee a level of homogeneity of functions (logging, debug level messages, email output, etc.).

Below is what I have so far. Any thoughts on improvements/additions?


##########################################################################
## Script Name : template.ps1 ##
## Created : 05/02/2008 ##
## Author : John McDevitt ##
## Function : sample script to be used as a start for all scripts ##
## : in production. ##
## : ##
## Usage : how to call this script (e.g. arguments required or ##
## : accepted) ##
## : ##
## Host/path : where is this script located ##
## : ##
## Notes : Update the help message with meaningful text for ##
## : your script. ##
## : ##
## : include debugging messages by calling debug_msg ##
## : e.g., debug_msg("about to do something weird") ##
## : include log messages by calling log_msg. Log file ##
## : is configurable, but defaults to a scriptname.log ##
## : in the current directory (will be a share soon). ##
## : All debug messages go into the log prefaced with ##
## : DEBUG: ##
## : ##
## : you will probably need to update the param block, ##
## : even though it is above the "your code here" block ##
## : ##
## Update Log : ##
## : ##
##########################################################################
param (
[switch]$debug,
[string]$mailto,
[string]$logfile
)

function Usage
{
""
"Describe the purpose of this script"
""
"Usage: template.ps1 -option <value> "
""
"Required Parameters:"
" -option <value>: Describe the options and their expected values here"
""
"Optional Parameters:"
" -mailto user@domain: User/group to send a copy of any debugging messages"
" or log info to."
""
" -debug: Enables debug messages -- useful for tracing code execution"
""
" -? : Display this usage information"
""
""
exit
}

function log_msg ($message) {
$message >> $logfile
if ($mailto) { $script:email_body = $script:email_body + $message + "`n" }
}

function debug_msg ($message) {
if ($debug) {
$message = "DEBUG: " + $message
$message
log_msg $message
}
}

log_msg ("started execution at " + (get-date))

if ($logfile -eq "") {
$logfile = $($MyInvocation.mycommand.name) + ".log"
}

debug_msg "logfile is $logfile"

##########################################################################
## YOUR CODE BEGINS BELOW THIS LINE ##
##########################################################################

if (( $ARGS -eq '-?') -or ( $ARGS -match "help" )) {
Usage
}

##########################################################################
## YOUR CODE ENDS ABOVE THIS LINE ##
##########################################################################

log_msg ("completed execution at " + (get-date))
if ($mailto) {
send-smtpmail -to $mailto -smtphost mailhost.yourdomain.com -from $mailto -subject $($MyInvocation.mycommand.name) -body $email_body
}

Thursday, May 1, 2008

Reach out and touch someone

A follow on to my previous post. It's great to be able to find a specific copy of a process running on a remote machine (UserA's copy of Notepad, for instance), but the real benefit is being able to do something about it.

Below is kill_processes.ps1. You'll note that it's nowhere near as clean as my pkill for windows script. It turns out that to do much of anything on a remote machine with PowerShell, you need to go through WMI. Basically, this script is a more flexible version of the kill scripts. This one takes four parameters:
  • computername -- defaults to localhost

  • notuser -- this is a pattern for users to ignore. defaults to SERVICE or SYSTEM

  • user -- this is a pattern for users to find. It is required, and throws an exception if not provided

  • name -- this is a pattern for the process name to find and kill. It is required and throws an exception if not found.

Having both the notuser and user parameters is pretty redundant, but it's protection against a bad pattern for the user parm. I didn't want to put this script in the wild and have someone run it like .\kill_processes.ps1 -computername domaincontroller -user "ser" -name "*" when trying to kill all of sergey's processes -- bye-bye domain controller is NOT my goal.

param (
[string]$computername = "localhost",
[string]$notuser = "SERVICE|SYSTEM",
[string]$user = $(throw "enter the user name that started the process"),
[string]$name = $(throw "enter the process name to kill")
)
gwmi win32_process -computername $computername|
where {($_.getowner().User -notmatch $notuser) -and ($_.getowner().user -match $user) -and ($_.name -match $name)} |
foreach {$_.Terminate() >$null }

Tuesday, April 29, 2008

Remote process list

I have frequently looked for a good way to be able to tell someone what processes where running on a remote (windows) machine. The pslist command from sysinternals got close, but it was hard (impossible?) to show who was running a given process, or to ignore (or see) only a given user's processes.

For example, if you're interested in userA's perl command on machine1, pslist will tell you this:

C:\utils\pstools>pslist.exe \\machine1 perl

PsList 1.26 - Process Information Lister
Copyright (C) 1999-2004 Mark Russinovich
Sysinternals - www.sysinternals.com

Process information for machine1:

Name Pid Pri Thd Hnd Priv CPU Time Elapsed Time
perl 4444 8 1 28 928 0:00:00.015 3:18:10.509
perl 5680 8 1 29 928 0:00:00.046 2:18:14.806
perl 5112 8 1 30 928 0:00:00.015 0:18:21.197


So which one is userA's? And who do the others belong to? Re-enter powershell. Below is the start of a script for hunting down this info. It defaults to looking at the current machine and filtering out system processes and services, but can be run against any machine and ignore any given user. I'll probably alter it to pay attention to a user instead of ignoring a user or users before I'm done.

param ( [string]$computername = "localhost", [string]$ignore = "SERVICE|SYSTEM" )
gwmi win32_process -computername $computername|
where {$_.getowner().User -notmatch $ignore} |
foreach {write-host ($_.getowner().User),$_.processid,$_.name,$_.commandline}


Running it against machine1 gives this:

[utils:\testing\powershell]> utils:\testing\powershell\processes.ps1 -computername machine1
0 System Idle Process
userA 5128 cmd.exe cmd /c C:\esp.bat "\\server1\sharea\bin\perl.exe \\server1\sharea\file_watch.pl \\server2\shareb\file1.txt 1020M"
userA 4444 perl.exe \\server1\sharea\bin\perl.exe \\server1\sharea\file_watch.pl \\server2\shareb\file2.txt 1020M
userA 5208 cmd.exe cmd /c C:\ESP.BAT "\\server1\sharea\bin\perl.exe \\server1\sharea\file_watch.pl \\server2\shareb\file3.txt 360m"
userA 5680 perl.exe \\server1\sharea\bin\perl.exe \\server1\sharea\file_watch.pl \\server2\shareb\file4.txt 360m
userA 6040 cmd.exe cmd /c C:\esp.bat "\\server1\sharea\bin\perl.exe \\server1\sharea\file_watch.pl \\server2\shareb\file5.txt 240m"
userA 5112 perl.exe \\server1\sharea\bin\perl.exe \\server1\sharea\file_watch.pl \\server2\sharea\file6.txt 240m


Pretty interesting, given that it shows 6 copies of perl where pslist only shows three. The difference is that pslist was good about only getting the perl commands, but ignored the .bat files that turned around and called perl.

Friday, April 25, 2008

pkill for windows

One of my absolute favorite tools on solaris is pkill. Without it, when you want to send a signal to a process (most likely to kill it) you need to figure out the process ID and then use that:

kill -HUP 1234

If you want to kill all of a type of process (e.g. all the sshd's), you end up doing something like this:

for i in `/usr/bin/ps -o pid,args -ef |grep sshd |awk '{ print $1 }'`; do kill $i;done

With pkill this becomes:

pkill sshd

Understand why I like it? Well, I just learned the powershell equivalent, and am PSYCHED.

Stop-Process (Get-Process winword).id

Bye-Bye Word.

"Powerful" end of lines

A follow up to this post.

Translate end of lines to windows style:

ConvertTo-WindowsLineEnding unixfile.txt windowsfile.txt

Need to convert it back?

ConvertTo-UnixLineEnding windowsfile.txt unixfile.txt


Think I'm going to like this tool...

Thursday, April 24, 2008

Powershell

I'm in a class for the second half of the week, the upshot of which is I've got a new scripting language to blog about... :-)

Here's the start of a script I'm converting to Powershell from perl. It examines my IP address, and determines what should be started (or stopped) when I'm at work.

The perl variant looks like this:

$address = gethostbyname("L353K79XPL");
$address = join(".",unpack("C"x4,$address));
if ($address =~ /123.321/) { ## fake subnet, sorry
debug_msg("on work network");
StartService("", "Wuser32");
StartService("", "ASMAgent");
StopService("", "RapApp");
StopService("", "VPatch");
StopService("", "BlackICE");
StopService("", "tunnelguardservice");
}


Here's the powershell version. It might not look it, but getting the IP is much more understandable to me.

if ((get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = true")[0].IPAddress -match "123.321.\d+.\d+")
{
write-host "at work"
start-service Wuser32
start-service asmagent
stop-service rapapp
stop-service vpatch
stop-service blackice
stop-service tunnelguardservice
}


More fun to come.
counter free hit invisible