After working a while with Powershell and doing some time expensive jobs like getting the quota of all Microsoft servers I came to the point when i was looking for some parallelisation in Powershell. My problem was the quota script, that executes the following command for each file server:
dirquota Quota List /Remote:$server
Actually we have a couple of servers and need to get the quota from all servers to calculate the file system usage and built a daily statistic.
The first script collected the data from each server. Since this is a boring job, because reading a file stream and writing it to a local file is really boring, even for a computer. I wanted to accelerate this step. So i got in touch with Powershell and jobs.
Jobs are (dont kill me) distantly related with multithreading in .NET. Indeed, multithreading in .NET has many more advantages and features than in powershell, but as a beginner (some years ago
) in development i didn't do anything more than just starting threads and wait until they are finished (Yep, invokation was a foreign concept to me). So this relation is not so far out.
Anyway, jobs do a great job if you have time consuming actions over and over again.
First of all I declaered a global array for my fileservers.
$global:DomainFileservers = @("fs1","fs2",...,"fsX")
The next step was to call the dirquota command for each server at the same time. The first problem i ran into was the parameterlist of fileservers, because a Powershell job executes only commands in a scriptblock. But after a little research i found the solution.
So i defined my scriptblock like this:
$GetQuotaList = {
param($server)
write-host "Get Quota From $server"
dirquota Quota List /Remote:$server >> "C:\temp\Quota\data\QuotaFromQuotaList$($server).txt"
}
As you can see, there is a param function inside the scriptblock that handles my submitted arguments. The final foreach for this example call look like that:
foreach ($server in $global:DomainFileservers){
Start-Job $GetQuotaList -ArgumentList $server
}
It's important to submit your parameter with the start-job flag "-ArgumentList". So every scriptblock gets it's own server parameter. Now we have to wait until every job is finished. With the command "get-job" you get every started job. The only thing we have to do here is to set a filter for the running processes within a while loop.
While (Get-Job -State "Running")
{
write-host "." -nonewline
Start-Sleep 5
}
Powershell loops here over and over again until every job has the state completed, or failed (maybe not a really good idea, but..
). After all jobs are done we remove the complete joblist with the command:
remove-job *
The next step I've done was putting every quotalog into one big file, and split them into 10 equal pieces. After i had 10 quota files with the same size i started 10 powershell jobs which evaluate the data and generated me an output file for our statistic software.
As you can see, jobs are very easy to handle and can speed up your automation process extremely. I decreased the runningtime for quota evaluation process from round about 2 hours down to 25 minutes.
Here is a little statistic insight of my home directory.

Quota Statistics
The other day I saw a script output that was colored with magenta. I was a bit impressed by the readability of the output messages. As I looked to the source code I saw something (ugly) like:
# Testoutput
write-host -nonewline "Creating Link from";write-host -nonewline -f magenta " locationXYZ";write-host -nonewline " to";write-host -nonewline -f magenta " target UVW"
The output looked like:

Powershell colorizing string output
The Output is not so bad, but the effort is enormous. I came to the idea to encode the colorsettings within the output string. So I ended up with the following Script:
function write-chost($message = ""){
[string]$pipedMessage = @($Input)
if (!$message)
{
if ( $pipedMessage ) {
$message = $pipedMessage
}
}
if ( $message ){
# predefined Color Array
$colors = @("black","blue","cyan","darkblue","darkcyan","darkgray","darkgreen","darkmagenta","darkred","darkyellow","gray","green","magenta","red","white","yellow");
# Get the default Foreground Color
$defaultFGColor = $host.UI.RawUI.ForegroundColor
# Set CurrentColor to default Foreground Color
$CurrentColor = $defaultFGColor
# Split Messages
$message = $message.split("#")
# Iterate through splitted array
foreach( $string in $message ){
# If a string between #-Tags is equal to any predefined color, and is equal to the defaultcolor: set current color
if ( $colors -contains $string.tolower() -and $CurrentColor -eq $defaultFGColor ){
$CurrentColor = $string
}else{
# If string is a output message, than write string with current color (with no line break)
write-host -nonewline -f $CurrentColor $string
# Reset current color
$CurrentColor = $defaultFGColor
}
# Write Empty String at the End
}
# Single write-host for the final line break
write-host
}
}
Now it's possible to use a single string to encode the color by seperating each color with a hashtag (#).
Here is an example:

colorizing string output with colorvariables
The syntax is very simple. Choose a color for the next characters and put it into hashtags (#). To stop coloring the characters just put another hashtag behind the colored characters to reset the foreground color to its default.
# Testing
write-chost "#green#All these characters are green until you end the coloringprocess with a hashtag.# Now everything is default again"
If you want to use any other charater, or command for the coloring, just replace the hashtag of the split function in line 20 with your character/command. I think this is a usefull function, because script output is often needed and coloring the important parts of the output is sometimes very helpful
Edit: I edited the script to work even if you pipe a string to write-chost like:
PS C:\> "#green# Everything is ok at #magenta#MyServer#" | write-chost
I wanted to rename a couple of computers within our active directory. After some research i figured out thats it seems to be nearly impossible to rename a computer by just touching one object: the ad computer object, or the computer (client) itself. My first thought was "ok, you have to rename both objects, rejoin the computer and hope everything works". But that's no solution, that makes me happy, because the more steps you do, the more problems can occure. For examples, what happens if the computer has to reboot, after renaming to get correct rejoined? Do i have to create a local admin account at the clientside to have permissions after the computer lost his connection to ad? And so on...
I played around, renamed the ad object, rebooted it - negative, the computer has to be joined again. After that i tried it the "bottom up" way by renaming the computer by hand, and rebooted it instantly. While the computer was shutting down i noticed, that the computerobject in active directory was renamed before the computer was finished with its shutdown process.
So i tried this several times and every time the ad computerobject was renamed properly. YAY!
I had my solution. It can't get more easy to rename a computer without rejoining it.
After this conclusion i tried to do it remotly with powershell, so i googled and found some sites about using netdom.exe. But, yep, right, calling a remote program was not the way i want to solve this problem
. I found some information on using wmi that suits me, so i started writing a powershell script to test it. As expected it's a bit complicated to rename a computer within a domain by wmi. You have to overcome 3 "hurdles":
- Use Authentication
- Using username and passwort of an administrative account
- Reboot the computer instantly after renaming
Here is a script, that does everything you want. Rename the computer and reboot it if renaming was successfull. Look at the variable section at the script header and fill in your administrative credentials (~domain admin)
#############################################################################################
# Parameterlist
#############################################################################################
param([string] $computername,[string] $newcomputername)
#############################################################################################
# Module import
#############################################################################################
# For checking if the computer exists in AD
import-module activedirectory
#############################################################################################
# Variable
#############################################################################################
$ADUserName = "ad\myuser" # Don't forget to add your domain
$ADUserPassword = "MySecretPW"
#############################################################################################
# POWERSHELL Ping tool to check if a computer/ip/hostname is online
#############################################################################################
function isComputerOnline( $comp ){
trap [Exception] {
return $false
}
if ( $(new-object system.net.networkinformation.ping).send( $comp ).status -eq "Success" )
{
return $true
}else{
return $false
}
}
#############################################################################################
# Remote Rename Computer by WMI
#############################################################################################
function COMPUTER_RENAME([string] $oldComputerName, [string] $newComputerName){
# should implement further checks for the computername. If computername uses illegal characters and if computername is longer than 15 characters
# More information here: http://labmice.techtarget.com/articles/computernaming.htm
$oldComputerName = $oldComputerName.replace(" ","");
$newComputerName = $newComputerName.replace(" ","");
# // Check if the computer is online
if ( isComputerOnline $oldComputerName ){
# // Check the active directory if a computer with your provided newcomputername already exists.
# // If you want to use the script without active directory just comment (use: #) this line and the corresponding "else" loop
if ( ! (Get-ADObject -ldapfilter "(CN=$newComputerName)") ){
# // Handle every upcoming error as a stop failure, so you can trap it
$ErrorActionPreference = "Stop"
try{
# // Get the WMI Object of the remote computer
$ComputerWMIObject = Get-WmiObject Win32_ComputerSystem -ComputerName "$oldComputerName" -Authentication 6
if ( $ComputerWMIObject ){
# // Rename the Computer Object with your or some admin credentials (Yes, Passwort is the second parameter and username the third )
$result = $ComputerWMIObject.Rename("$newComputerName", $ADUserPassword , $ADUserName )
# // Switch Case for the returnvalue of computer renaming function
switch($result.ReturnValue)
{
0 {
# // Reboot the computer instantly if renaming was successfull
Get-WmiObject Win32_OperatingSystem -ComputerName "$oldComputerName" |
ForEach-Object {$restart = $_.Win32Shutdown(6)}
write-host "Computer $oldComputerName was renamed ($newComputerName) and restarted"
}
5 { write-host "Computer was not renamed. Please check if you have admin permissions (ReturnCode 5)"; exit; }
default { write-host "ReturnCode $($result.ReturnValue)"; exit;}
}
}else{
write-host "Couldn't create WMI Object on $oldComputerName"
}
}catch{
write-host $_
}
}else{
write-host "There is already a computerobject with the name $($newComputerName)"
}
}else{
write-host "Computer is offline!"
}
}
if ( $computername -and $newcomputername ){
COMPUTER_RENAME $computername $newcomputername
}else{
write-host ""
write-host "Script Usage: RenameComputer.ps1 -computername ""Current-Computer-Name"" -newcomputername ""New-Computer-Name"""
write-host ""
}
Due to some issues with copy and paste of this sourcecode, there is a download (zip) available here -> Rename Computer Powershell Script.
After you have downloaded and extracted the zip file, edit the script with your favorite editor (I always use notepad++ or notepad). At the beginning of the script, there is a section called variable. You have to enter your Active Directory username (with ad forrest) and the corresponding password.
Now start your Powershell and go to the directory where your script is and start it with the following command:
.\RenameComputer.ps1 -computername "CurrentCompName" -newcomputername "NewCompName"
There is no check on the computername yet, so every computername you submit is more or less valid.
More information about the limitations of a computername can be found here: http://support.microsoft.com/kb/909264/en-us
Feel free to use this script and leave me a comment!