Export a list of Exchange Contacts via PowerShell

Today, I needed to export a list of all of our contacts’ email addresses. The easiest way to do this was with PowerShell from the Exchange Management Shell:

Get-MailContact | Select-Object -Property PrimarySmtpAddress | Out-File c:tempcontacts.txt

Some more reading:

PowerShell: Determine the external IP of a remote system

I needed a quick way to determine the external-facing IP of a remote system. As long as you’re not going through a proxy, you can use sites like whatismyip.com or type “my IP address” into Google. I wanted to achieve this without having to get onto the machine and fire up the web browser.

The following one-liner will query the whatismyip.org API from a remote system, and return just the IP:

Invoke-Command -ComputerName <remote PC name> -ScriptBlock {(Invoke-RestMethod -Uri "http://www.realip.info/api/p/realip.php").IP}

PowerShell: Replacing Invalid Characters in a Filename

I’m currently writing a script that requires me to create folders based on the contents of a CSV-dump from SQL. One thing I need to do is check that each item I’m pulling out of the CSV will translate to a valid Windows folder name.

One easy way to do it is by using the .NET System.IO.Path.GetInvalidFileNameChars() method. This method returns a System.Char array containing all of the characters that can’t be used in a file or folder name. Here’s a sample of the output:

 image

Assuming that the string we wish to interrogate for invalid characters is stored in a variable called $text, we’d use the following one-liner to loop through all of the invalid characters and replace them with another of our choosing. In this case, I’m replacing them with a period.

[System.IO.Path]::GetInvalidFileNameChars() | % {$text = $text.replace($_,'.')}

Here’s a screenshot of it in action: image

Determine the logged-on User’s AD group membership in PowerShell

I came across this great little nugget of a 1-liner while reading this blog post on automating Outlook Profile creation, so all props go to Travis Runyard for this one.

([ADSISEARCHER]"samaccountname=$($env:USERNAME)").Findone().Properties.memberof

To break it down, this is using the [ADSISEARCHER] type accelerator to create an instance of the DirectorySearcher classimage

The string specified directly after the accelerator denotes the search filter, so in this case, we’ll only be searching for objects with a samaccountname attribute that matches the current user’s logon name. image

There’s only ever going to be one object returned, so we use the FindOne method to return a single System.DirectoryServices.SearchResult object.
image

All that’s left after that, is to get the contents of the “memberof” property on that object.
image

In his blog post, Travis goes one step farther and uses a regex to remove the LDAP path elements like “CN=” which leaves us with just the group names. Very smart!

([ADSISEARCHER]"samaccountname=$($env:USERNAME)").Findone().Properties.memberof -replace '^CN=([^,]+).+$','$1'

If we store the results of this search in a variable, for example $userGroups, we can then check if the user is a member of a certain group:
image

Alternatively, you could use comparison operators like –contains, –ccontains for a case-sensitive comparison, or even –notcontains.

([ADSISEARCHER]"samaccountname=$($env:USERNAME)").Findone().Properties.memberof -replace '^CN=([^,]+).+$','$1' -ccontains "Colour Printing"

Log to Loggly from your PowerShell Scripts

I’ve been pondering how to keep track of the results of the various PowerShell scripts on my network. I first considered setting up Windows Event Log forwarding, but I deemed that a bit too complex just to log some information from my scripts.

I then found Loggly, a cloud-based logging service that has an easy-to-use JSON API. At the time of writing, they have a “Lite” plan that’s free, and includes 200MB of logged data per day, with 7 days of retention.

Here’s a quick and dirty function that I can include in my scripts to log errors and other information to Loggly, where I can then search and filter based on hostname and the source script.

Note that if you use a proxy on your network, you can specify one when using Invoke-WebRequest.

Here’s the function, which needs to be put at the top of your PowerShell script. Just remember to replace “CUSTOMER_TOKEN” with your actual customer token:

function LogToLoggly {
    param ($Message,$Type)

    $logURI = "https://logs-01.loggly.com/bulk/CUSTOMER_TOKEN/tag/powershell"
    
    # If we don't specify a type via parameter, assume it's information
    if ($Type -eq $null) { $Type = "Information" }

    $jsonstream = @{
        "timestamp" = (get-date -Format s);
        "type" = $Type;
        "source" = $MyInvocation.ScriptName.Replace((Split-Path $MyInvocation.ScriptName),'').TrimStart('');
        "hostname" = $env:COMPUTERNAME;
        "message" = $Message;
        "exception" = $Exception
    }

    $jsonstream | Invoke-WebRequest -Method Post -Uri $logURI
}

Here’s how you’d log an informational message:

LogToLoggly "This is a test log message"

And an error:

LogToLoggly "This is a test error message" "Error"

This is the JSON that gets sent to Loggly:

{
    "message":  "This is a test error message",
    "source":  "testing-1.ps1",
    "timestamp":  "2014-05-19T19:08:59",
    "exception":  null,
    "hostname":  "COMPUTER.LOCAL",
    "type":  "Error"
}

And this is what it looks like in Loggly:

2014-05-19 19_10_32-Loggly _ Search!

Reporting on Spiceworks tickets via SQLite and PowerShell

We’ve been using Spiceworks as our helpdesk ticketing solution for years, as it was way better than any commercial options back then, and it’s still doing the job well.

As we’re currently attempting to put more emphasis on support through the helpdesk, I thought I’d have a look at the built-in reports. While they’re great, there was nothing that suited my requirement of emailing each helpdesk operator their open tickets on a schedule. I also have several other requirements to do with SLAs, but I’m yet to implement solutions to those.

Continue reading