New Year, new opportunities

December 31, 2007

2007 was a very memorable year for me.  It started out with some big surprises that I never had to deal with before and looked like it was going to be a very difficult year.  Then it progressed into a newfound obsession for PowerShell, the start of this blog, a great change in career (within a company instead of between companies, a first for me), and now it is ending with much anticipation for what’s coming next (I’m still waiting to see if the only Christmas present I’ve been hoping for is coming…just a few more days to find out).

Looking back, it was definitely one of my most challenging years ever and after pushing myself pretty hard and taking a few risks ended up being one of the most rewarding as well.  And it will likely be a year I never forget.

Looking forward, I can’t wait to see what opportunities the New Year brings!

Happy New Year everyone!

Kirk out.

Technorati Tags: , ,


Quest AD Cmdlets gone gold!

December 21, 2007

It’s official!

The Quest AD Cmdlets are now RTM!  And they’re still 100% free!  If you are doing any scripting with Active Directory and PowerShell, these cmdlets are absolutely indispensable.

You can download the RTM version here.

Note: The initial post of the RTM version still had “ReleaseCandidate” as part of the msi filename.  This is still the RTM release.  The filename will be corrected shortly.

Kirk out.

Technorati Tags: , , ,


Dealing with comment spammers the PowerShell way

December 17, 2007

Since I started blogging earlier this year I’ve come to notice a two types of spam that I haven’t had to think about before: incoming link spam and comment spam.

Incoming link spam is quite interesting…links show up in my incoming link list trying to get me to be curious enough to click on those links and see what the source site is all about, which then results in trojan horses and other various nasty side effects detrimental to the health of your computer.  Curiosity killed the cat.  I learned that lesson the hard way quite a few months ago, and won’t get suckered by that one again.  I don’t think there is much I can do about that one either, so it’s best to simply be aware of it and only follow through links that look relevant to what you blog about.

Comment spam comes in one of two varieties: links to various sites selling pharmaceuticals or showing pornography (and probably also loaded with trojan horses and other equally nasty exploits), and content theft.  The link variety is just a minor annoyance which gets caught by my spam filters and automatically deleted.  The content theft variety is something that bothers me a little more though.  It comes in the form of a trackback link that would show up in my comments if I let it through.  The spam filters recognize it as spam, so I could just let it be automatically deleted, but I’m too bothered by the content theft to just delete the tracktrack comment.  What bothers me is that the site that is linking to my blog doesn’t care about me or PowerShell or PowerGUI or anything that I blog about.  They just want to get discovered through people following links in my comments or through search engines picking up their sites.  Then once you are on their site, they have advertisements everywhere waiting for you to click on them so that they get click-through income.  These individuals are trying to profit off of other bloggers hard work, not just by getting links on their site but by stealing their content, and that just doesn’t sit right with me.  And it must be working to some degree, because more and more of these types of sites pop up all the time.

Fortunately I have found through personal experience that you can successfully take action against the content theft sites.  I’ve had the blog entries linking to my blog removed from some sites (which now are shut down, so maybe removing the blog entries wasn’t enough for them), and I’ve had other sites shut down immediately and no longer available on the web.  This works because of policies that ISPs have against abuse, and fortunately spam is often considered as abuse in those policies.  An ISP that was used to host one of the sites that I’ve successfully had shut down has a strict no spam policy including the following penalties:

  • seizure of all on-premises equipment and data;
  • forfeit of all funds paid;
  • a demand for payment of a fine (to cover damage to our reputation)
  • $500/incident/spam clean-up fee, all of which is payable within 24 hours or will be referred to a collection agency;

That sounded like fair and just punishment to me, and they were very quick to shut down the site in question, so I recommend others try this as well.

If you have a blog and you want to take similar action against content theft, how do you go about doing it?  Well you could use a good whois database like http://ws.arin.net/whois/, enter the IP associated with the site that stole the content, go to the website of the ISP identified in the who, check out their abuse policy and then contact them with the details about the comment theft in hopes that they will shut them down.  Or you could use PowerShell to look after the retrieval of the whois record and have it open the website of the ISP for you as well as an email in your default email program already populated with the email address, subject and body.  Then it’s as simple as verifying the abuse policy on the ISP’s site, clicking send on that email and with a little luck, presto!  Another spammer gone! :)

Since this seems to be a trend that will only continue, I started writing a script to do this for me a little while ago and when I saw that content-theft trackback spam message in my spam folder this morning, I decided today I would finish it.  Well now it’s finished and ready for you to customize for your blog.

The script uses two functions to do its work:

  1. Get-WhoIsRecord - used to retrieve the whois record for a site identified by a dns name or an ip address from the specified whois database and build a custom PowerShell object with the data in that record.
  2. Fight-ContentTheft - used to extract the dns name from the offending blog url that is passed in, lookup the whois record, extract the abuse email address from that whois record, open the website for the ISP that hosts the offending blogger in the default browser and open a new email message in the default email program with the abuse email address, an appropriate subject and a message body already entered into the message.

I need to note that this script has only been tested for my own comment spam and therefore may need to be tweaked to work in your environment.  Also it has a two placeholders in the message body waiting for you to provide your blog address as well as your signature.  And finally note that this may not work through firewalls depending on their configuration.  But it can work because I’ve gotten it to work from my laptop, so if you have some issues I encourage you to troubleshoot and see if you can get it working if you want to fight this type of spam like me.

Since the script is a little long, I’m keeping it in an attachment to this post so that you can download it more easily.  The attachment is a text file so you’ll need to save it as a ps1 file if you want to run it within PowerShell.

To get the script, click here.

Enjoy!

Kirk out.

Technorati Tags: , ,


When in doubt, ask the community

December 16, 2007

In case you hadn’t noticed already, there are many people who are ready, eager and waiting to help you with your next PowerShell problem.  The list of PowerShell enthusiasts is long and growing steadily, and they’re responsiveness to questions is incredible.  So responsive in fact that it seems like there is a competition to see who can answer questions first!

Leverage that community!

If you have questions, post them somewhere.  If you have scripts that you can’t quite work out, share them and get the community to help.  If you think you can work it out but just want a few pointers, search the community and find if the answers you seek have already been posted somewhere.  There’s really no reason to wait.  If you’re stumped, ask for help and you’ll get it.

Here’s an updated (thanks to Hal for pointing out some important ones that I missed) list of just a few places you can go if you have questions:

  1. PowerShell Newsgroup - a very active newsgroup for discussions about anything related to PowerShell
  2. PowerGUI Community - the place to go to share PowerGUI PowerPacks or to ask questions about PowerGUI and Quest AD cmdlets
  3. PowerShell Community - a non-profit PowerShell Community site with forums for anything related to PowerShell, a script repository, and much more
  4. PowerShell Blog Search - a custom Google search of many PowerShell blogs; created and maintained by Brandon Shell (PowerShell MVP)
  5. The PowerScripting Podcast - a semi-weekly podcast about everything PowerShell; Jonathan Walz and Hal Rottenburg are the hosts, and this is a great source of current PowerShell information, interviews, tips and tricks, etc.
  6. PowerShell IRC channel (#PowerShell on irc.freenode.net) - an IRC chat room with many PowerShell savvy people hanging out, discussing PowerShell, ready to answer your questions right then and there.

If you’re looking for help on PowerShell, please use the community.  You’ll be thankful you did!

Kirk out.

Technorati Tags: , , , , ,


Sometimes you just want a property

December 7, 2007

On the PowerShell newsgroup I often (really often) see users new to PowerShell getting stuck transitioning from the point where they have a set of objects to creating an array of the values of one of the properties on those objects.  For example, if you are using the free Quest AD cmdlets and you want to retrieve a list of group names to which a member belongs, you can do this:

(Get-QADUser “Poshoholic”).memberOf | Get-QADGroup | ForEach-Object {$_.Name}

This seems simple enough but many people get stuck going from a collection such as that returned by Get-QADGroup to an array of names such as that returned by the ForEach-Object cmdlet shown in this exanple.  The missing piece of knowledge is usually understanding how to use ForEach-Object to extract the member you want.  Also, even when you know how to use ForEach-Object, I find it a little inconvenient to use ForEach-Object with a script block just to pull out a property name, or to put brackets around part of a command so that I can access members on the result.  And I find myself doing this quite regularly in my PowerShell work.

To both save myself from having to use ForEach-Object or brackets when all I want is to invoke a member on an individual object or a collection of objects, and to share this convenience with others, I have created an Invoke-Member function that makes the one-liner I listed above a little more elegant and PowerShelly.  Here’s an updated version of the one-liner I used above, revised to use the Invoke-Member function:

Get-QADUser “Poshoholic” | Invoke-Member memberOf | Get-QADGroup | Invoke-Member name

This one-liner retrieves an AD user named Poshoholic and from that user it retrieves the memberOf member value.  That value contains an array of DNs for groups to which the user belongs.  For each of those DNs, the one-liner then retrieves the AD group object and once it has that it extracts the name of that group.

The Invoke-Member function can be used to invoke any type of member, including both properties and methods (e.g. Invoke-Member “Open()”).  It can also be used to invoke nested members (e.g. Invoke-Member parameters.parameter).  And it can take input from the pipeline or alternatively through the InputObject parameter.  When using the InputObject parameter, the object is passed in as one object, even if it is a collection.  When using the pipeline collections are passed into Invoke-Member one item at a time.

[Update] Since this was originally posted, Xaegr pointed out in the comments that the PowerShell team already had published a function on their blog that does this, and that he has modified that function to support multiple members.  You can find that blog entry here, and Xaegr’s modifications can be found in the comments in this blog entry.  The problem with those two implementations of the … function  is that they don’t support calling methods and they don’t support nesting members, both of which are supported by the Invoke-Member function.  I liked Xaegr’s idea to support retrieving multiple members at once though, which I think could be useful for simplified output of member values without member names attached to that output.  I also think it would be useful when working with certain datasets that have different members with the same type that you want to do something with in one pass through that data set, or when you want to execute multiple methods in sequence during one pass through a dataset.  I added support to the Invoke-Member function so that it would support multiple members as well.

Here is the updated source code for the Invoke-Member function as well as a script to create im and … aliases for the Invoke-Member function:

Function Invoke-Member {
    param (
        [string[]]
$name = $(throw $($(Get-PSResourceString -BaseName ‘ParameterBinderStrings’ -ResourceId ‘ParameterArgumentValidationErrorNullNotAllowed’) -f $null,‘Name’)),
       
$inputObject = $null
   
)
    BEGIN {
    }
    PROCESS {
       
if ($inputObject -and $_) {
           
throw $(Get-PSResourceString -BaseName ‘ParameterBinderStrings’ -ResourceId ‘AmbiguousParameterSet’)
           
break
        } elseif ($inputObject) {
            foreach ($member in $name) {
               
Invoke-Expression “`$inputObject.$member”
           
}
        }
elseif ($_) {
           
foreach ($member in $name) {
               
Invoke-Expression “`$_.$member”
           
}
        } else {
           
throw $(Get-PSResourceString -BaseName ‘ParameterBinderStrings’ -ResourceId ‘InputObjectNotBound’)
        }
    }
    END {
    }
}
if (-not (Get-Alias -Name im -ErrorAction SilentlyContinue)) {
    New-Alias -Name im -Value Invoke-Member -Description ‘Invokes a member of each of a set of input objects.’
}
if (-not (Get-Alias -Name-ErrorAction SilentlyContinue)) {
    New-Alias -Name … -Value Invoke-Member -Description ‘Invokes a member of each of a set of input objects.’
}

Note that this function uses the Get-PSResourceString function that I published recently in my Cmdlet Extension Library (you can read more about what this library contains here).  If you want to use the Invoke-Member function by itself, you will have to change the calls to Get-PSResourceString with static strings that you want to throw on error instead.  To simplify sharing the Invoke-Member function with the community I have added it to my Cmdlet Extension Library and uploaded the updated version to the PowerShell Community site.

Thanks to Joel Bennett for his article about writing functions that can also be executed in the pipeline.  I didn’t end up using Joel’s template as is because I wanted Invoke-Member to follow the model of other cmdlets that have the InputObject parameter, but it was my starting point before I went through numerous revisions and ended up with the final version shown above in the Invoke-Member function code.

Comments/suggestions are welcome and appreciated, as always.

Kirk out.

Technorati Tags: , , , , ,