Showing posts with label Scripts. Show all posts
Showing posts with label Scripts. Show all posts

Tuesday, January 10, 2012

Harnessing the Power of PowerShell to Load-balance Sophos Servers

At work we have a decent-sized Sophos installation.  This means that we have to use message relays to manage the status traffic back and forth between the Enterprise Console and the clients.  I recently discovered that although I could use groups to point client updates to their local server for updating, the message routers weren't affected.  As a result almost all clients ended up using the same server as a message relay.  I confirmed with my TAM that this feature is by design, so I set out to fix it with a script.  What I ended up with is basically what you see below.

A few things worthy of note:

  • I've pretty much standardized on using that logging boilerplate for most of my scripts.  It makes it easy to log errors and insert debug statements at the code as I'm writing so that I can always set -loglevel to 'debug' later when troubleshooting.
  • I made the caller pass the name of the mrinit.conf file so that I could create one small SCCM package for the script with all five different mrinit.conf files.
  • If you decide to do this, don't use the mrinit.conf file from the root of the package directory on the Update server.  There should be an mrinit.conf file in the rms subfolder.  Use that one.  If it isn't there, then you might not be configured to use a message relay, and this script won't help you until you are.



WARNING!  ACHTUNG!  AVISO!  LUU Y!
I am doing the QA and testing for my organization.  I make no guarantees that this script will work for yours.  Sophos is a temperamental beast, and you should do the due diligence to test and do the QA and do whatever modifications it takes to make it work for yours.  You may also wish to consult with your Sophos TAM before undertaking a project like this.


Wednesday, August 12, 2009

Use PowerShell to Get Local Group Members from a Remote Computer

I had a friend ask me recently how to get a list of administrators from a server.

"That's easy" I thought. "You just have to... um... actually..."

It turns out that this can be frustratingly difficult in PowerShell, so I wrote this module to make it easier when I'll need to do this in the future. It's comprised of three functions:
  • Add-NoteProperty: About 90% of the time when I want to create a custom PSObject and add some properties, they're all NoteProperties. This function makes adding a property easy, like so: Add-NoteProperty $my_object 'PropertyName' $property
  • Get-COMProperty: This is a kludgy hack to get around the fact that members of groups gotten through ADSI get returned as __ComObject objects, and you have to call the InvokeMember() static method of the class in order to get at their properties. Now I can just do this: Get-COMProperty $com_object 'PropertyName'
  • Get-LocalGroups: This is the function that we needed. It returns a list of custom PSObjects representing the local groups on a server, and each one has a property called Members that is a list of custom PSObjects for each member, including the Name, Domain, and ADSPath. From there, you can use whatever method you want to the object you want, whether it's using Get-QADUser for domain users, or whatever, for example: Get-LocalGroups computername.
If you're using Powershell v2 CTP3 or higher, you can use help on each of the functions to see examples if you forget. You can download the module from Poshcode.org here.



Tuesday, May 19, 2009

Get-ChildItemRecurse Update

Someone named CrazyDave made an important update to the script I made yesterday. I was checking for the specific type System.IO.DirectoryInfo. The problem with this is, it unnecessarily limits my script to being used on files and folders. Get-ChildItem makes no such distinction. It can be used on any PSDrive.

So here is the corrected version: http://poshcode.org/1115 (highlighted line is the old one)



Monday, May 18, 2009

A Get-ChildItem Wrapper That Lets You Say How Deep to Recurse

I noticed that a couple of people hit my blog that seemed to be looking for a script like this, and it turned out to be an interesting exercise.

Notice that I took the fileglob and moved it down the script rather than just using it in my call to Get-ChildItem. I had to do that because otherwise I would only have been recursing through directories that matched the pattern, and I wanted to recurse down into all directories, but only send those entries that match the pattern down the pipeline.

The function is here, for those that don't see it inline in the post: http://poshcode.org/1113



Sunday, May 3, 2009

Use PowerShell to Recursively Get Group Members

I've noticed looking at my StatCounter logs that a few people found my post on recursion while looking for a way to recursively get group members in PowerShell, so I whipped up this script.

Note that it depends on the AD Cmdlets from Quest, and that it works in much the same way as the example. Whenever it finds that one of the group members is a group, it calls itself on that group and adds the result to the list of users.

Also note the technique of using a hashtable to keep a list of unique users. It's common to store a list in an array and use use -contains and -notcontains to find out if an item already exists, but this is tremendously inefficient compared to using a hashtable as an index, especially as the list of users gets longer, because it has to keep cycling through the list over and over. It's a trick I picked up in Perl that works equally well here.



Tuesday, November 4, 2008

Windows Forms with PowerShell

SAPIEN came out with an awesome form builder for PowerShell called PrimalForms.  I decided to give it a whirl.  It really makes short work of making a native Windows GUI.  For my first project I made a quick and dirty app that allows me to get disk fragmentation data for a remote server using the DefragAnalysis method of Win32_Volume.  I used the PrimalForms GUI to create my form, and then I only had to fill in the button1 click handler and the form load handler, add a small function for printing to the text box, and voila!






Friday, October 31, 2008

Defrag Your Servers Remotely with PowerShell

Another quick and dirty script.  I've got a couple servers that are running applications that suffer from disk thrashing, and are running Windows Server 2003.  A defrag tends to noticeably increase performance.  With this script I can kick off a defrag from my desk whenever I want.






Sunday, July 6, 2008

UPDATE: Importing Scripts as Libraries, Part Deux

I forgot to mention that the most convenient place to put this function is in your profile. What's that? You don't have a profile? Well What are you watiing for?

Make a folder inside your Documents folder called WindowsPowerShell. Inside that folder create a script called Microsoft.PowerShell_profile.ps1. Think of it as .bashrc for PowerShell.

I've uploaded my profile as an example here.

Also, note that I'm searching under the path pointed to by the PSPATH environment variable. I have it set to C:\PSLibs in my profile.

Importing Scripts as Libraries, Part Deux

In this post I showed how using the dotsource operator lets us run a script and import all of its named objects from the script scope into the global scope. The effect is as if instead of calling a script you just typed its contents at the prompt.

This set off a lightbulb in my head, and gave me the solution to something I'd been pondering for a while: how to import scripts as libraries (modules, etc, whatever we're calling them these days). You see, if I come up with a really neat function I don't want to have to cut and paste it into every script that uses it, and if I come up with a lot of content, I'd like to be able to share them with people.

This is what I came up with, and it incorporates a few new tricks I picked up along the way to trying to solve this problem. The desired result: I can type in 'import-script scriptname.ps1' and it is automatically imported without having to know the full path to the file.

Some highlights:
  • Test-Path is a nice cmdlet for checking the existence of a path on any PSDrive, so that means any Enviornment variable, registry key, file object, etc.
  • Exceptions work, but are really hard to get straight. Check out this link for some details.
  • The (, (command)) syntax is how I'm forcing the variable to be an array. I might get rid of it later, I think now that I'm using boolean comparison instead of looking for the length of the array to check if any files were found.
  • In most examples people use Write-Error instead of echo to print error messages, but I wanted to have the error show up clearly without all of the red words and extra garbage.
  • Note that I'm searching everything under the folder pointed to by the PSPATH environment variable. I point mine to C:\PSLibs by default.

function Import-Script ([string]$script) {
  $local:ReportErrorShowSource = 0

  # Clean up the Exception messages a bit
  trap [Exception] {
    $errmsg = "`n`tError importing '$script': "
    $errmsg += ($_.Exception.Message + "`n")
echo $errmsg
break
  }

  # Check PSPATH
  if (-not (Test-Path "Env:pspath")) {
    throw "PSPath environment variable not set."
  }elseif (-not (Test-Path $env:pspath)) {
    throw "PSPATH environment variable points to invalid path." 
  }

  $files = (, (dir $env:pspath $script -Recurse))

  # Make sure we find a single matching file
  if ($files.Length -gt 1) {
    throw ([string]$files.Length + " files of name '$script' found in PSPATH.")
  }elseif (-not $files) {
    throw "No files named '$script' found in PSPATH."
  }

  # Do the needful
  . $files[0].FullName
}

The DotSource Operator

A while back I asked Stephen Ng if he had seen any documentation on importing scripts as modules or libraries, and he hadn't. I sort of filed it away for future reference, knowing that if we went down this path we'd probably want to find some way to keep a repository of PowerShell scripts for common use.

Fast forward to a few days ago, when the latest entry from The PowerShell Guy showed up in my Google Reader. In his Get-IpConfig function (which I'll discuss in a later post) he has a line with an open parenthesis followed by a dot.wasn't the most readable line of code in the world anyway, but as I was mentally parsing it I hit a snag. I had no idea what that dot was doing just after the open parenthesis.

As it turns out, this is what's known as the dotsource operator. The dotsource operator (I wish people would stop and think a bit when choosing these terrible names) is like the ampersand (&) operator, in that it executes the script or code block that comes after it, but it also declares all variables in the global scope. Why would you want to do this?

Let's use a terrible example and say I have a script that will get a list of users and groups on a computer. I have a script, Get-UsersAndGroups.ps1. It creates two variables, $users and $groups, and exits. I want to be able to use those variables, so I run the following:

PS C:\> .\Get-UsersAndGroups.ps1
PS C:\> $users

I get nothing back, even though I know I created the variable. $users was created in the script's scope, though, and once the script finishes running it takes its variables with it.

I have three options here:
  1. Change my script to send the variables to the output pipeline. Maybe this script is being used by other people, though, so I don't necessarily want to change the pipeline.
  2. Explicitly declare the variables as $global:users and $global:groups in the script. The problem with this is that I don't necessarily always want to declare these variables in the global namespace.
  3. Use the dotsource operator.
The way to use the dotsource operator is like this:

PS C:\> . .\Get-UsersAndGroups.ps1
PS C:\> $users
Tim
Administrator
Alfred E. Neuman

(Note that there is a space between the dots there, otherwise I'd just be telling PowerShell to look in the parent directory.)

To wrap this up, we can take all of this newfound knowledge and see how you can create libraries in PowerShell. By dotsourcing (yes, people even use it as a verb, ugh) a script with functions and constants in it, we can import the functions in a way that makes them usable by our scripts, that way we can reuse the code.

The one big problem is that PowerShell has no concept of a /lib directory that it looks for modules in by default, so you must know the path to the script you will be importing. I'll leave that as a future exercise, but I have a spark of an idea in my head of how we can create a function that looks up a script by its name to import and distribute it using a common company Profile so that all company PowerShell Scripters have a common location for libraries, like sitecustomize.py in Python.