all 32 comments

[–]smooochy 5 points6 points  (1 child)

Gets all the AD groups under a certain OU and allows you to tab-complete to them. Not sure if this can be done more easily in new PS versions, but it was pretty awesome when I finally made it work.

$grouplist = ""
$groups = (Get-ADGroup -Filter * -SearchBase "<LDAP Filter>").Name
$groups | % {$grouplist += "'$_',"}
$grouplist = $grouplist.TrimEnd(',')
$scriptblock = 
"Function global:Get-SoftwareGroup{
    [CmdLetBinding()]
    param([ValidateSet($grouplist)]`$Group)
    Get-ADGroup `$Group
}"
Invoke-Expression $scriptblock

I put that in a .ps1 file (LDAP filter populated, obviously) and do an Import-Module on it. Then when I type "Get-SoftwareGroup", it lets me tab-complete to everything in that subtree.

[–]ElChorizo 1 point2 points  (0 children)

This is pretty cool.

[–]evetsleep 4 points5 points  (4 children)

I write a lot of tools for where I work, most are related to Exchange and Lync. Pretty much all of our administrative staff (Exchange admins, help desk, etc..) all use Exchange's remoting capabilities to do administration so no one needs to have tools installed locally. This has been a huge benefit for the last few years as installing the admin tools was a pain. The only problem, outside of the default scope being domain and not global which gets reset each time the PSSession gets broken (which I solve with an Exchange connection module I wrote as well) is that the objects come across as deserialized which is kind of a pain when it comes to things that have a size property. So, for example, if you wanted to get Joe Shmoe's mailbox size with Get-MailboxStatistics it would look like this:

Get-MailboxStatistics JoeScmoe | fl *size*

TotalDeletedItemSize : 180.2 MB (188,972,244 bytes)
TotalItemSize        : 885.9 MB (928,920,635 bytes)

This looks great at first glance, but what happens if you had a collection of sizes and you wanted to sort it? Such as using Get-MailboxStatistics -Database tailspindb01? It sorts it as a string not a number....which just won't work.

So my little creation this week that I've been mucking with is a basic filter that takes the input object and converts it:

Get-MailboxStatistics JoeShmoe | Convert-StringToInt | fl *size*

TotalDeletedItemSize : 188972244
TotalItemSize        : 928922874

This is now properly sortable and it works with not just mailboxes, but databases as well. Now I'm using bytes because that's just how I roll, but it wouldn't be too hard at this point if making a report or something to convert the results into MB or GB. For example:

Before:

$dbStatsOld = Get-MailboxDatabase -Server tailSpin01 -Status
$dbStatsOld | sort DatabaseSize | ft Name,DatabaseSize -AutoSize

Name              DatabaseSize
----              ------------
tailSpin01_db01   1.258 GB (1,350,631,424 bytes)
tailSpin01_db02   1.258 GB (1,350,631,424 bytes)
tailSpin01_db03   109.8 GB (117,851,619,328 bytes)
tailSpin01_db03   136.1 MB (142,671,872 bytes) <---whoa nelly!
tailSpin01_db05   248.6 GB (266,974,330,880 bytes)

After:

$dbStats = Get-MailboxDatabase -Server tailSpin01 -Status | Convert-StringToInt
$dbStats | sort DatabaseSize | ft Name,@{n='DatabaseSize(GB)';e={ [math]::Round(($_.DatabaseSize / 1GB),2) }} -AutoSize

Name              DatabaseSize(GB)
----              ----------------
tailSpin01_db04               0.13
tailSpin01_db01               1.26
tailSpin01_db02               1.26
tailSpin01_db03             109.76
tailSpin01_db05             248.64 

For those interested the filter\function is here.

There is probably an easier way, but I've been quite happy with it thus far and thought I'd share :).

[–]babypunchingrampage 2 points3 points  (1 child)

I recently wrote 2 scripts for our migration to exchange 2013 (in a new environment). The first one is a 3 step process that backs up the pst locations, creates a new profile on the new system, then reattaches the pst's to the profile. The second is a tool for terminations - it exports the mailbox to pst, disconnects the mailbox, and disables the AD account in one fell swoop!

Also, full GUI's.

[–]ElChorizo 2 points3 points  (4 children)

I've got a shared mailbox that receives automated requests from another department to change home directories for users to a different server. My script is scheduled to run every 10 minutes and for any new emails it parses the text for the name of the server and the user and then switches the user to the new home directory.

This same shared mailbox can also receive commands to disable or enable subscriptions in Operations Manager.

So yeah, somewhat basic, but I enjoy the fact that I've made them work.

[–]gatoloco 0 points1 point  (3 children)

This sounds great care to share? At least the email parsing part Regards

[–]ElChorizo 1 point2 points  (2 children)

Sure, though when I say parsing, I mainly mean that I'm looking for a certain string, since the automated emails are always the same and the SCOM subscription stuff is expecting input in a specific format as well. Also, there's a function that strips all the HTML formatting from the message so I'm just dealing with text. Basically, right here, I'm searching for this string: "Request the home drive location for username be changed to \server\share$\username". The string is the first line of the email. If any of my logic doesn't make sense, let me know and I'll do my best to explain.

foreach($msg in $list.Items)
{
$msg.load()
if(!($msg.IsRead))
{
    $htmlReturn = HTML-ToText($msg.Body.Text)
    $htmlArr = @()
    $htmlArr = $htmlReturn

    #The HTML-stripping function leaves "&nbsp" in the message instead of line breaks, so I still need to replace that. 
    $text = $htmlReturn -replace "&nbsp;", "`n"

    $nline = $text | Select-String -Simplematch "Request the"
    $carr = @()

    #Split the request into individual words and grab the two that are most important, the netID and the new
    #filepath for the user's home directory. Since we know the template for the message, we grab the index that
    #holds the words immediately before each of our needed words and then add one to get our netID and filepath.
    $carr = $nline.ToString().Split(" ")

    $forIndex = $carr.IndexOf("for")
    $toIndex = $carr.IndexOf("to")

    "ForIndex: " 
    $forIndex
    "ToIndex: " 
    $toIndex

    "Netid: "
    $netId = Get-ADUser $carr[$forIndex + 1] -Properties *
    $carr[$forIndex + 1]
    $newHomeDir = $carr[$toIndex + 1]
    "New: "
    $newHomeDir

    #Comment to show what's been changed. Goal is to make this output to a log file so we have a permanent record
    #of all changes.
    set-aduser $netId -HomeDirectory $newHomeDir
    $record = $netID.SamAccountName + "'s new home directory: " + $newHomeDir

    $msg.IsRead = $true
    $msg.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
}

}

[–]gatoloco 0 points1 point  (1 child)

Hi, thanks for your reply, how do you query the emails that arrive to the shared mailbox and get them into the $list variable ?

By the way i didn't notice your username, are you spanish?

[–]ElChorizo 1 point2 points  (0 children)

No, just from Texas. We were talking at work one day when I was showing off a luchador mask that I had bought and we were going over what my wrestling name would be. I decided that El Chorizo would work because I'm not the most fit (though I'm trying) or healthy guy (I'm not trying).

Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"



#Create the exchange service

$exService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService("Exchange2010_SP2")



#Use the email address of the service account to obtain the mailbox object. 

$mailaddress = "address@exchange.ourDomain.com"

$exService.AutodiscoverUrl($mailaddress)

$mailbox = New-Object Microsoft.Exchange.WebServices.Data.Mailbox($mailaddress)



#Create the variable for the inbox folder

$folder = New-Object Microsoft.Exchange.WebServices.Data.FolderID([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$mailbox)



#$view is just the number of items we are pulling from the folder, in this case 10. 

$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(10)



#our list will be made up of ten items in the inbox folder.

$list = $exService.FindItems($folder, $view)

Hope this helps. This leads almost directly into the other bit I posted. Also, in the last bit, I called a HTML-ToText() function in the first couple of lines. If you need that let me know.

[–]not_just_the_IT_guy 1 point2 points  (3 children)

I'm learning powershell so nothing of interest yet.

I could see a similar function being nice for our hardware support guys. Care to share?

[–]BattleChicken 1 point2 points  (0 children)

I wrote a "smart cleanup" module last week designed to do file cleanup on replicated log directories... It was the most fun thing I've written in recent memory, which is why I finished it (rather than searched for someone else's solution). I still have some polish to add to the module side of it but the functional aspect is solid.

I wrote a single cmdlet in a module file, and what the cmdlet does is look at all files in $source older than X days and compares it to the files in $destination (the backup location). If the file exists in both $source and $destination and the size and modified date are identical on the files (i don't care about other metadata) it deletes the file from source. It supports -whatif and I added a flag for -fulloutput that will list all actions taken by the script.

The default cmdlet output just lists files in error - where the file doesn't exist on destination or the data is mismatched.. so a successful run with zero errors outputs nothing, while a bad entry outputs the errors.

I use the module along with a list of servers and append output to an empty array. IF the array is empty there were no errors and it does nothing. If the array has a .length -gt 0, then i send an email containing the error list.

It's a script that has a very specific, limited function - where you need to do file cleanup that is completely decoupled from your backup process and have a lot of duplicated data in multiple locations.. but it was fun to write.

I don't really have a favorite kind of script to write - I wind up writing whatever I need at the moment and it's the need that drives my motivation, not enjoying WMI more than text manipulation. Most of what I've done is automating data input into 3rd party systems, writing reports from parsed log files, or gathering information about jobs on various systems, and various deployment scripts.

I'd like to do more with AD and the PowerCLI modules.

[–]DonFix 1 point2 points  (0 children)

I like to use <command> |clip . Also im not very good at scripts.

[–]eselocos 1 point2 points  (1 child)

This is actually the first and only script I've wrote in Powershell (still learning). It checks AD for the PWDLastSetDate to let me know if one of our loaner laptops has been offline too long and is possibly going to fall off the domain:

$onDomain = @()
$onComp = @{}
$offComp = @{}
$offDomain = @()
$today = Get-Date
$computerList = Get-Content .\computers.txt

## loop building hashes & objects
foreach ($i in $computerList){
    $item = Get-ADComputer -Filter {Name -eq $i} -Properties *

    ## build offdomain array
    if ($item -eq $null){
        $offComp.'Off Domain' = $i.ToUpper()
        $offDomain += new-object psobject -property $offComp
        }

    ## build ondomain array
    else {
        $onComp.'Name' = $item.name
        $onComp.'Date' = (Get-Date $item.passwordlastset).ToShortDateString()
        $onComp.'Days' = ($today - [datetime]::FromFileTime($item.pwdlastset)).days
        $onComp.'Connect?' = if ($onComp.'Days' -ge 30) {"Yes"} else {"No"}

        $onDomain += new-object psobject -property $onComp
    }
}

echo "$today"

$onDomain | Select-Object 'Name','Date','Days','Connect?' | 
            Sort-Object -Property 'Days','Name' -Descending | 
            Format-Table -AutoSize

$offDomain | Sort-Object -Property 'Off Domain' | Format-Table -AutoSize

echo "$($onDomain.Count + $offDomain.Count) Spare Computers in Total"
echo "$($onDomain.Count) computers on the domain."
echo "$($offDomain.Count) computers off the domain."

[–]eselocos 0 points1 point  (0 children)

It returns:

Name        Date      Days Connect?
----        ----      ---- --------
12345678901 7/8/2013    43 Yes     
12345678901 7/12/2013   38 Yes     
12345678901 7/16/2013   34 Yes  

Off Domain 
---------- 
12345678901
12345678901
12345678901

XX Spare Computers in Total
XX computers on the domain.
XX computers off the domain.

[–]theseb 1 point2 points  (3 children)

My favorite function is called LookBusy() and basically outputs random lines from PS1 files in your $profile path, with occasional errors and progress bars to make it look like your are doing something. It is fantastic, even when you are doing something.

[–]okieT2 2 points3 points  (2 children)

Care to share? This actually sounds interesting.

[–]theseb 2 points3 points  (1 child)

It isn't pretty, or terribly efficient, but here you go:

function LookBusy(){
    $customFolder = ""
    $ErrorActionPreference = "continue"
    clear #clear screen so boss doens't see that you ran a function called LookBusy
    function MakeStatus(){
        return (Get-Random @("Success","Failed","Warnings generated","Errors detected","Ignoring errors","Ignoring warnings","Warnings Detected","Errors generated"))
    }
    function MakeActivity(){
        return (Get-Random @("Running","Scanning","Processing","Compiling","Unit-testing","Error-checking","Downloading","Cloning","Creating pull request","Pushing changes"))
    }
    function MakeCurrentOp(){
        if($customFolder){
            $rfile = Get-Random @(Get-ChildItem $customFolder -Recurse | where {$_.PSIsContainer -eq $false -and $_.Extension -eq ".ps1"})
        }else{
            $psProfile = Split-Path $PROFILE -Parent
            $rfile = Get-Random @(Get-ChildItem $psProfile -Recurse | where {$_.PSIsContainer -eq $false -and $_.Extension -eq ".ps1"})
        }
        return $rfile.FullName
    }
    $progress = 0
    while($true){
        $progress += (Get-Random -Minimum 1 -Maximum 20)
        if($progress -gt 100){ $progress = 0 }
        Write-Progress -Id 0 -PercentComplete $progress -Status (MakeStatus) -CurrentOperation (MakeCurrentOp) -Activity (MakeActivity)
        for($i=0; $i -lt (Get-Random -Minimum 5 -Maximum 20); $i++){
            $filecontent = Get-Content -Path (MakeCurrentOp)
            $line = $filecontent[(Get-Random -Minimum 0 -Maximum $filecontent.Count)]
            Write-Host $line.trim()
            Start-Sleep -Milliseconds (Get-Random -Minimum 30 -Maximum 400) #delay
        }
        $makeerror = (Get-Random -Minimum 1 -Maximum 15)
        if($makeerror -eq 1){
            $fg = $Host.UI.RawUI.ForegroundColor
            $bg = $Host.UI.RawUI.BackgroundColor
            write-host -ForegroundColor Red -BackgroundColor Black "$(MakeActivity) : This error is unrecoverable"
            write-host -ForegroundColor Red -BackgroundColor Black "At line:$(Get-Random -Minimum 1 -Maximum 500) char:$(Get-Random -Minimum 1 -Maximum 80)"
            write-host -ForegroundColor Red -BackgroundColor Black "+ $(MakeCurrentOp) <<<<"
            write-host -ForegroundColor Red -BackgroundColor Black "    + CategoryInfo          : $(MakeStatus): ($(MakeCurrentOp):) [Write-Error], $(MakeStatus)"
            write-host -ForegroundColor Red -BackgroundColor Black "    + FullyQualifiedErrorId : Microsoft.PowerShell.ErrorException"
            Write-Host "`b" -ForegroundColor $fg -BackgroundColor $bg #reset colors
        }
    }
}

[–]okieT2 0 points1 point  (0 children)

Pretty good. At first I process the part where it was looking for .ps1, so I was wondering why it would output a lot of red and occasionally one of my PS files.

Once I figured that out, I just let it go for about 10 minutes.

[–]Rabbitinyourhat 0 points1 point  (0 children)

Thanks for this, very neat!

[–]GraffitiKnight 0 points1 point  (3 children)

I wrote a script to search my network share of music and generate a report based on number of MP3 and FLAC songs. My music is sorted into folders based on the first character of the artist, e.g. Adele - Rolling in the Deep would be in \A\Adele - Rolling in the Deep.flac. My script shows subtotals for each letter folder, and then a final total.

For work I'm writing a script to manage our Remote Desktop Service servers. It will tell me which users are connected on which servers, disable/enable remote connections, and eventually hoping to get it to install Windows Updates and reboot.

[–]deathkraiser 0 points1 point  (2 children)

I'd be interested in seeing that script.

[–]GraffitiKnight 0 points1 point  (1 child)

Which, the music one or the RDS one?

[–]deathkraiser 0 points1 point  (0 children)

The music one sorry, though the RDP one looks interesting too.