21 April 2016

How to get the neighboring computer to talk using psexec, PowerShell and System.Speech

Let’s assume, you have domain network and you are using domain admin to do the trick. Also the neighbors computer must be running quite fresh windows version (i tried windows 10 only, let me know when older ones also work).

First of all you need psexec from sysinternals to run something on other computer. http://live.sysinternals.com/ is one good starting point for getting that tool.

Next open your command prompt as the domain admin user and type the following command (yes, it is a one-liner):

psexec \\neighbor cmd /c powershell [Reflection.Assembly]::LoadWithPartialName('System.Speech');$object = New-Object System.Speech.Synthesis.SpeechSynthesizer;$object.Speak('i know what you did last summer')

where neighbor is the name of the neighboring computer and last string is the text to speak. After some waiting the neighboring computer should talk to the default audio device.

25 January 2016

PowerShell speed optimizing on Active Directory cmdlets

Lets imagine, you have a task to find some AD user accounts based on a criteria and perform a simple task for each of them. My case is to clear manager for all user accounts, where extensionattribute3 is not present. This needs to be done regularly. So the time spent on this task should be minimized.

Initial version took about 160 seconds to run:

Measure-Command {
$users = Get-ADUser -Filter * -Properties extensionAttribute3 | ? extensionAttribute3 -ne 'Nortal Employee'
$users | foreach {Set-ADUser $_.SamAccountName -Clear manager -WhatIf}
}

Lets try to eliminate users, who already have manager cleared and not clearing the manager for them. This run took about 20 seconds because, instead of thousands of users, only few hundred were actually touched:

Measure-Command {
$users = Get-ADUser -Filter * -Properties manager,extensionAttribute3 | ? extensionAttribute3 -ne 'Nortal Employee' | ? manager
$users | foreach {Set-ADUser $_.SamAccountName -Clear manager -WhatIf}
}

Next iteration would be piping all users directly to Set-ADUser cmdlet, but this would not work straight forward, we will need Select-Object with ExpandProperty. This run took about 5 seconds because Set-ADUser was loaded only once instead of hundred times:

Measure-Command {
$users = Get-ADUser -Filter * -Properties manager,extensionAttribute3 | ? extensionAttribute3 -ne 'Nortal Employee' | ? manager
$users | select -ExpandProperty SamAccountName | Set-ADUser -Clear manager -WhatIf
}

Get-ADUser will still get all users and the filtration is done in PowerShell. This can be optimized to pass LDAP query to AD to return only needed user accounts. This run took about 2 seconds:

Measure-Command {
$users = Get-ADUser -LDAPFilter "(&(manager=*)(!extensionAttribute3=*))"
$users | select -ExpandProperty SamAccountName | Set-ADUser -Clear manager -WhatIf
}

This command can be converted to oneliner. This will have no noticeable effect on command duration, but looks perhaps cleaner:

Measure-Command {Get-ADUser -LDAPFilter "(&(manager=*)(!extensionAttribute3=*))" | select -ExpandProperty SamAccountName | Set-ADUser -Clear manager -WhatIf}

So our initial code was later optimized to to run nearly 100 times faster. Not a bad thing for a scheduled task running daily or hourly.