Archive for the 'Examples' Category

PowerShell script to set Skype status text to latest blog or twitter update

In my current company (Jelastic) we have something happening to us all the time: latest blog posts, awards, media mentions, etc. We are doing a decent job pushing these to various social media, but I also wanted to get these to my contacts in Skype (Skype gives you the ability to set your status text in your profile).

Below is the PowerShell v3 script that I wrote today to do that! 🙂

It takes the latest item from my blog and twitter feed, sees which of them is fresher, and (unless the tweet is just my reply to someone) pushes that to Skype (the property is called MoodText).

Here’s the script:

# Set-SkypeStatusText.ps1
# Gets latest post from RSS feed (e.g. blog) and Twitter
# Picks whichever is the latest and sets it as status text (MoodText) in Skype
# (unless the latest tweet is a reply)
# NOTE: On x64 boxes, use PowerShell x86 (for Skype compat)
# (c) 2012 - Dmitry Sotnikov

# Customize these for yourself
$myblog = ""
$myTwitterHandle = "DSotnikov"

# Get the blog feed
$blogFeed = Invoke-RestMethod $myblog

# Get the twitter feed
$twitterFeed = Invoke-RestMethod `

# If twitter is more recent and not a reply (does not start with @) use it
if (($twitterFeed[0].pubDate -gt $blogFeed[0].pubDate) -and
($twitterFeed[0].description[$myTwitterHandle.Length+2] -ne "@")){
$latestPost = $twitterFeed[0].description.Substring($myTwitterHandle.Length+2)
} else {
$latestPost = "$($blogFeed.Item(0).title): $($blogFeed.Item(0).link)"

# Set the status in twitter
$skype = New-Object -ComObject Skype4Com.Skype
$skype.CurrentUserProfile.MoodText = $latestPost

Now if you want to have this happen automatically you can just schedule it using Windows Task Scheduler.


  • Make sure that you use 32-bit (x86) version of PowerShell if you are on 64-bit Windows – otherwise Skype object will not get found (so the filepath for the Windows task on x64 Windows will likely be %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe)
  • Either sign your script or set ExecutionPolicy to RemoteSigned – otherwise the script execution will fail.

New in PowerShell 3: Parse HTML without IE object (unless a local file)

Remember how in PowerShell v1 and v2 we used to have to create Internet Explorer object each time we wanted to parse HTML page? This kind of works but has a few inconveniences such as having to insert Start-Sleep every now and then because IE can be busy and fail if you request too much from it too quickly.

In PowerShell v3, for web pages, things become much easier. Just do:

$p = Invoke-WebRequest ""

And $p.ParsedHtml.body will let you iterate though all web page elements!

However, there is a scenario in which you will have to revert to the old IE ways – local files. If the HTML file is on your local disk, $p will not have the ParsedHtml property. And you will have to use the IE COM object like you did in earlier versions of PowerShell:

$ie = new-object -com "InternetExplorer.Application"
# The easiest way to accomodate for slowness of IE
Start-Sleep -Seconds 1
# The easiest way to accomodate for slowness of IE
Start-Sleep -Seconds 1
$ParsedHtml = $ie.Document

Happy scripting!

Parsing LinkedIn html pages with PowerShell

A couple of weeks ago we posted a job opening on LinkedIn (were looking for a person to be in charge of our Jelastic‘s professional services), and it turned out that while LinkedIn jobs attract a lot of applications, the site itself does not make it easy to process them afterwards. You get CVs in email, and they also post a list of applicants with email addresses, phone numbers, titles, etc. – but there is no way to export the list to, say, Excel. In our case, we really wanted to have the data exported, so we could jointly work on a shared spreadsheet and everyone involved could grade each applicant and add notes to the table.

Being a PowerShell guy, I wrote the script below that does the scraping for me. 🙂 Basically, I just saved the page with the list of applicants to my local disk and found that in their html, each applicant information is contained in vcard element, which has class name with LinkedIn URL and the actual name, and then elements with email and phone number:

So all my script has to do is: create an IE object and then use it to find the corresponding fields, then create custom objects from them, add them to the collection, and export it to CSV. Here’s the code – hope it helps you solve similar tasks when other sites do not provide good export capabilities:

$ie = new-object -com "InternetExplorer.Application"

# The easiest way to accomodate for slowness of IE
Start-Sleep -Seconds 1


# The easiest way to accomodate for slowness of IE
Start-Sleep -Seconds 1

$doc = $ie.Document

# Get a collection of vcard elements
$cards = $doc.body.getElementsByClassName("vcard")

# This will be our collection of parsed objects
$processesCards = @()

# Iterate through the collection
for ($i=0; $i -lt $cards.length; $i++) {

 $itm = $cards.item($i)

 # Get the 'name' element that has the applicant name and URL
 $name = $itm.getElementsByClassName("name").item(0).

 # If you want you can output the name to the screen 
 # so you know where you are

 # Get the phone number and email address
 $phone = $itm.getElementsByClassName("phone").item(0)
 $email = `

 # Below is PowerShell v3 notation. 
 # In v2, replace '[pscustomobject]' with 
 # 'new-object psobject -Property' 
 $obj = [pscustomobject] @{"name"=$name.outerText; 
                           "phone"= $phone.outerText }

 $processesCards += $obj


# Export to CSV - which you can open in Excel
$processesCards | Export-Csv D:\Temp\linkedin.csv 

Group Membership Unions and Intersections

A friend of mine recently asked for one-liners for Active Directory group membership union and intersection.

These are the one-liners which I sent him:

Group Union – users present in either of the groups (he needed a list of DNs of direct members of two groups):

(Get-QADGroupMember GroupA –Type user) + (Get-QADGroupMember GroupB –Type user) | 
 Select-ExpandProperty DN
 Sort | Select-Unique

Group Intersection – users present in both groups at the same time:

Compare-Object (Get-QADGroupMember GroupA –Type user) `
 (Get-QADGroupMember GroupB –Type user) `
 -ExcludeDifferent -IncludeEqual | Select-ExpandProperty InputObject

You can obviously tweak them to add indirect users (with -indirect parameter) or enabled only (-enabled), etc. – see Get-QADGroupMember help for all options.

I am pretty sure that there are multiple ways to skin these cats – so if you have better alternatives – please post these in the comments.

Find everyone rolling up to me

Yesterday someone asked me to help create a distribution list for everyone reporting to a particular manager (directly or indirectly). Needless to say, that PowerShell makes getting a list of such user accounts a piece of cake!

Here’s the quick script (using AD cmdlets) which I emailed back:

function Get-QADIndirectReport {
param ($Identity)
  # Find all direct reports
  Get-QADUser -Manager $Identity | ForEach-Object {
      # Output direct report
    # Then recursively call this function for all
    # reports of this report
    Get-QADIndirectReport -Identity $_


# usage example
Get-QADIndirectReport 'Dmitry Sotnikov'

Basically, AD cmdlets natively can retrieve all direct reports, and I have created a function which keeps going deeper level-by-level getting everyone reporting indirectly as well.

You can then take this a few steps further. For example, say, you want to get a list of users you could then just copy/paste into Outlook. Simply select the Email property from the user objects and ask PowerShell to put semicolon between the addresses:

# get a list of addresses for an email message
(Get-QADIndirectReport 'Dmitry Sotnikov' | 
  Select-Object -ExpandProperty Email) -join '; '

Or you could indeed use the list to populate a group:

# add everyone to a group
Get-QADIndirectReport 'Dmitry Sotnikov' |
  Add-QADGroupMember DmitrysReports

Or you could further restrict the list by City, Department and so on by simply tweaking Get-QADUser parameters in the code above. PowerShell is super-flexible!

Loose or exact matching in AD cmdlets

Do you know what is the difference between Get-QADComputer A2101 and Get-QADComputer -Name A2101?

When we were designing Quest AD cmdlets we did our best to be as forgiving as possible. So for example you can just do Get-QADUser and get the first 1000 user objects (or whatever is the default number you set) retrieved. Or you can just do a Get-QADUser Dmitry and get all the Dmitry’s you have in your organizations (if you have any :))

In most cases, this forgiving nature is what you are in the command-line interactively managing your Active Directory. If you have ever tried using some other snapins/modules from other vendors who went a different route – you should have noticed how much difference this makes.

However, sometimes you do want to be explicit and just get the exact object you want and not just whatever matches. In this forum thread Jason was trying to update a set of computer records based on a CSV file he was importing.

Unfortunately for him, some of computer names in his environment include other names. E.g. he not only has a computer called ‘A2101’, but also ‘A21012’ and ‘A21013’.

Which means that Get-QADComputer A2101 returns all three of those:

Jason obviously did not need the other two computers and explicitly wanted to update just the one which matches exaqctly.

Luckily the workaround is very simple. If you know what you need – be explicit when you are asking for it. For example, if the you are identifying the computer by its Name – just tell us so and if no wildcards are used we will only retrieve the exact match:

Get-QADComputer -Name A2101 only returns one computer record with Name being A2101.

Happy scripting!

Find group members by location

Today I had to promote a local event to everyone on our cloud taskforce. The distribution list we have for everyone interested in cloud projects is quite large so I thought I would share this one-liner with you.

The first version I tried was quite straight-forward – simply get all team members and filter out the members based on their city:

Get-QADGroupMember Cloud -Indirect |
    where { $_.City-eq "Aliso Viejo" }

However, this actually was quite slow – because the group is big and all the filtering was happening on the client side (all objects were extracted from domain controller and then filtered by PowerShell on my workstation). The solution is to use parameters of the initial Get cmdlet. Get-QADGroupMember unfortunately does not have the City parameter yet, so I used the universal LdapFilter parameter to do the proper filtering.

Get-QADGroupMember Cloud -Indirect -LdapFilter '(l=Aliso Viejo)'

This second one-liner performed almost twice faster – so this is the one I would recommend for large group use!


Managing Certificate Revocation Lists and Certificate Stores

Vadims has published a couple of articles with great examples of how to use PowerShell to manage CRLs (Certificate Revocation List) and local certificate stores.

CRL tasks include:

  • Importing CRL:
$crl = Import-QADCertificateRevocationList -File C:\pica-1.crl
  • Reviewing CRL details:
$crl | format-list *
  • Add CRL to local certificate store:
Add-QADCertificateRevocationList -CRL $crl -Store $store
  • CRL removal:
Get-QADCertificateRevocationList -Store $store |
  where-object {$_.IssuedBy -like "sysadmins*"} |
  Remove-QADCertificateRevocationList -Store $store
  • CRL export:
Export-QADCertificateRevocationList -CRL $crl -File c:\customcrl.crl
  • Publishing CRL:
Publish-QADCertificateRevocationList -CRL $crl -CAName CustomCAName
  • Unpublishing CRL:
Get-QADPKIObject CDP |
  Get-QADCertificateRevocationList |
  where-object {$_.IssuedBy -like "sysadmins*"} |
  Unpublish-QADCertificateRevocationList -CAName "CustomCAName"

For certificate store management, Vadims goes through:

  • Exploring certificate stores:
Get-QADLocalCertificateStore -StoreLocation LocalMachine -StoreName My |
  • Adding certificate stores:
New-QADLocalCertificateStore -StoreLocation CurrentUser -StoreName CustomStoreNameToAdd
  • Removing certificate stores:
Remove-QADLocalCertificateStore -StoreLocation LocalMachine -StoreName CustomStoreToDelete

For all these, Vadims is providing tons of details so I highly recommend checking those out:

Managing Certificate Revocation Lists (CRL) with PowerShell

Managing Certificate Stores with PowerShell

Resolving external accounts in domain groups

You do not have to do anything to do that. If you group contains an account from a trusted domain, good old

Get-QADGroupMember MyGroup

will resolve foreign security principals and show them as regular users.

However, in some cases – for example for performance reasons – you might not want AD cmdlets to perform these look-ups in trusted domains. For that, you just need to use the KeepForeignSecurityPrincipals parameter that we added in AD cmdlets 1.4:

Get-QADGroupMember MyGroup -KeepForeignSecurityPrincipals

Resolving Foreign Security Principals

Starting with version 1.4 AD cmdlets can retrieve and provide detailed information on all properties for foreign security principals.

When you add a user from a trusted domain to a group in your domain, AD creates a local auxilliary object – foreign security principal – to represent this external account. You can essentially think about this object as a pointer to the actual account in a trusted domain. You can read more about them in the Security Principals section of this TechNet article.

Now QAD cmdlets can resolve these “pointers” and show you real accounts to which they point.

For example, this command will retrieve all foreign security principals which you have in your domain (i.e. all foreign accounts ever granted any rights) and try to resolve them to external accounts from original domains:

Get-QADObject -ResolveForeignSecurityPrincipals -Type foreignSecurityPrincipal

My Recent Tweets


The posts on this blog are provided “as is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer - WSO2 or anyone else for that matter. All trademarks acknowledged.

© 2007-2014 Dmitry Sotnikov

March 2023

%d bloggers like this: