Friday, March 18, 2022
Run powershell specific function from task scheduler
Executable is powershell.exe. Arguments:
-command "& { . "c:\location\to\script.ps1"; my_function_name }"
Powershell speed hacks
Powershell can be painfully slow when dealing with larger arrays, reading files and listing large directories. Here are some workarounds.
Arrays
Slow:
$myarray = @() foreach ($x in $y) { $myarray += $x }
Much faster is working with an arraylist:
$myarray = [System.Collections.ArrayList]@() foreach ($x in $y) { $null = $procarray.Add($x) }
Reading files
Slow:
get-content $filename
Fast:
([System.IO.File]::ReadAllLines($filename))
Listing large directories
Slow:
$items = get-item "\\server\share\*.csv" | sort LastWriteTime
The fastest workaround i’ve been able to find is actually using a dos prompt. Use dir switches for sorting purposes.
Note: dir returns just text, while get-items returns objects with all sorts of properties. It depends on your use case whether this hack is actually usable or not.
$items = cmd /r dir "\\server\share\*.csv" /OD /B
Friday, July 18, 2014
ParentContainer
Quest active directory powershell module has this nice property for user and computer objects: ParentContainer
Microsoft’s native ActiveDirectory module doesn’t.
I’m using this property a lot because it looks much more friendly than the CanonicalName.
Here’s a simple function to achieve the same.
function CanonicalName_to_ParentContainer ($cname) { try { $lastslash = $cname.lastindexof("/") $cname.substring(0,$lastslash) } catch { $cname } }
Tuesday, December 10, 2013
Powershell IP address computations
Very neat function for powershell ip computations:
source: technet
function Get-IPrange { <# .SYNOPSIS Get the IP addresses in a range .EXAMPLE Get-IPrange -start 192.168.8.2 -end 192.168.8.20 .EXAMPLE Get-IPrange -ip 192.168.8.2 -mask 255.255.255.0 .EXAMPLE Get-IPrange -ip 192.168.8.3 -cidr 24 #> param ( [string]$start, [string]$end, [string]$ip, [string]$mask, [int]$cidr ) function IP-toINT64 () { param ($ip) $octets = $ip.split(".") return [int64]([int64]$octets[0]*16777216 +[int64]$octets[1]*65536 +[int64]$octets[2]*256 +[int64]$octets[3]) } function INT64-toIP() { param ([int64]$int) return (([math]::truncate($int/16777216)).tostring()+"."+([math]::truncate(($int%16777216)/65536)).tostring()+"."+([math]::truncate(($int%65536)/256)).tostring()+"."+([math]::truncate($int%256)).tostring() ) } if ($ip) {$ipaddr = [Net.IPAddress]::Parse($ip)} if ($cidr) {$maskaddr = [Net.IPAddress]::Parse((INT64-toIP -int ([convert]::ToInt64(("1"*$cidr+"0"*(32-$cidr)),2)))) } if ($mask) {$maskaddr = [Net.IPAddress]::Parse($mask)} if ($ip) {$networkaddr = new-object net.ipaddress ($maskaddr.address -band $ipaddr.address)} if ($ip) {$broadcastaddr = new-object net.ipaddress (([system.net.ipaddress]::parse("255.255.255.255").address -bxor $maskaddr.address -bor $networkaddr.address))} if ($ip) { $startaddr = IP-toINT64 -ip $networkaddr.ipaddresstostring $endaddr = IP-toINT64 -ip $broadcastaddr.ipaddresstostring } else { $startaddr = IP-toINT64 -ip $start $endaddr = IP-toINT64 -ip $end } for ($i = $startaddr; $i -le $endaddr; $i++) { INT64-toIP -int $i } }
Monday, May 13, 2013
Dump Exchange mailbox permissions
A complete script to first dump all exchange mailboxes to .csv and then enumerate all mailbox permissions.
It uses the Exchange 2010 management shell and Quest’s Active Directory Powershell modules.
Usage:
- Load the script in the ISE editor.
- Set the two global parameters
- Run the script
- first execute: dump_mailboxes (this wil generate a .csv with all mailboxes)
- then execuite: dump_all_mailbox_permission (this will generate a second .csv with all permissions. Open in Excel to filter)
echo "-" $global_ad_domain = "AD.CUSTOMER.LOCAL" $global_ad_short = "AD" ### Load Modules for Active Directory and Exchange 2010 if (!($QUEST_LOADED)) { Add-PSSnapin Quest.ActiveRoles.ADManagement Set-QADPSSnapinSettings -DefaultSizeLimit 0 $logged_on_to = $env:USERDNSDOMAIN if (!($logged_on_to -eq "$global_ad_domain")) { $user = read-host "Enter username in adusername format" $pw = read-host "Enter password" -AsSecureString connect-QADService -service '$global_ad_domain' -ConnectionAccount $user -ConnectionPassword $pw } else { connect-QADService } Set-QADProgressPolicy -ShowProgress $false $QUEST_LOADED=$TRUE echo "quest loaded" } if ($EMS_loaded -eq $NULL) { . 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1' echo "- Exchange Management Shell Loaded" Connect-ExchangeServer -auto $EMS_loaded = $true echo "- Exchange Management Shell Connected" } ### Functions function dump_mailboxes { $output_file = "d:\temp\mailboxes.csv" echo "Name`tAlias" >$output_file # $mailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox $mailboxes = Get-Mailbox -resultsize Unlimited foreach ($mailbox in $mailboxes) { $Name = $mailbox.Name $Alias = $mailbox.Alias echo "$Name`t$Alias" >>$output_file } } function dump_all_mailbox_permission { $output_file = "d:\temp\mailbox_permissions.csv" $lijst = import-csv -delimiter "`t" d:\temp\mailboxes.csv $aantal = $lijst.count $teller = 0 write-host "Aantal functionele mailboxen: $aantal" echo "Mailbox`tAuthType`tGroup`tSam`tType" >$output_file foreach ($regel in $lijst) { $teller++ $Alias = $regel.alias write-host "$teller / $aantal -> $Alias" mailbox_permissions $Alias >>$output_file } } function mailbox_permissions($mailbox) { if ($perms = get-mailboxpermission -identity "$mailbox" | where {($_.isinherited -eq $false) -and ($_.User -like "$global_ad_short\*")}) { foreach ($perm in $perms) { $usr = $perm.User.tostring() $typeusr = (get-qadobject -identity $usr -DontUseDefaultIncludedProperties).type $usr = $usr.replace("$global_ad_short","") $rights = $perm.AccessRights if ($typeusr -eq "group") { $members = get-qadgroupmember -identity "$usr" foreach ($member in $members) { $mbmrsam = $member.samaccountname echo "$mailbox`t$typeusr`t$usr`t$mbmrsam`t$rights" } } else { echo "$mailbox`t$typeusr`t`t$usr`t$rights" } } } } echo "-"
Wednesday, September 5, 2012
get size of directories with powershell, the stupid but fast way
All those ways to get the size of directories with powershell are extremely slow. Especially on network shares.
e.g.
$colItems = (Get-ChildItem C:Scripts | Measure-Object -property length -sum) "{0:N2}" -f ($colItems.sum / 1MB) + " MB"
Currently i’m harvesting through roughly 40TB of data and it’s taking me daaaaaaaaaays!
So i’m in desperate need of something faster.
Then i thought about robocopy. Robocopy gives great statistics. So if i do a “dry-run” (list-only, not really copy), i might get the information i need by parsing the output.
Choice of switches:
- /b = backup mode. Supposed to give me access to every file
- /l = list only/dry-run, not really doing the copy
- /mir = action what you would normally do when you would copy the data. This also dives into all subdirectories.
- /r:0 = no retries
- /w:0 = don’t wait on anything
- /ns /nc /nfl /ndl /njh = no logging of any kind. We only want the summary.
Then we get this piece of code (it could be a lot shorter, but i’m keeping it readable):
function get_size_of_dir_in_bytes_with_robocopy ($directory) { write-host "- $directory" -foreground "GREEN" [string]$result = robocopy /b /l /mir "$directory" "c:\whatever" /r:0 /w:0 /ns /nc /nfl /ndl /njh /bytes if (!($lastexitcode -eq 16)) { $pos = ($result).indexof("Bytes : ") $start = $pos + 8 $length = $result.length $end = $length - $start $newstring = ($result).substring($start,$end) $newstring = $newstring.trim() echo $newstring.split()[0] } else { echo "CANNOT ACCESS" } }
Wednesday, June 20, 2012
powershell and robocopy
Hmm seems like $lastexitcode is a builtin variable. Nice!
robocopy "\\serverA\shareA" "\\serverB\shareB" /MIR /R:0 /W:0 /MT:4 /NP /LOG:"d:\logs\shareA_to_shareB.log" | out-null interpret_robocopy_error $lastexitcode
and the function interpret_robocopy_error could be something quick’n'dirty like this:
function interpret_robocopy_error ([int]$errorlevel) { if ($errorlevel -eq 16) { echo " - Robocopy - ***SERIOUS FATAL ERROR*** "} if ($errorlevel -eq 15) { echo " - Robocopy - OKCOPY + FAIL + MISMATCHES + XTRA "} if ($errorlevel -eq 14) { echo " - Robocopy - FAIL + MISMATCHES + XTRA "} if ($errorlevel -eq 13) { echo " - Robocopy - OKCOPY + FAIL + MISMATCHES "} if ($errorlevel -eq 12) { echo " - Robocopy - FAIL + MISMATCHES "} if ($errorlevel -eq 11) { echo " - Robocopy - OKCOPY + FAIL + XTRA "} if ($errorlevel -eq 10) { echo " - Robocopy - FAIL + XTRA "} if ($errorlevel -eq 9) { echo " - Robocopy - OKCOPY + FAIL "} if ($errorlevel -eq 8) { echo " - Robocopy - FAIL "} if ($errorlevel -eq 7) { echo " - Robocopy - OKCOPY + MISMATCHES + XTRA "} if ($errorlevel -eq 6) { echo " - Robocopy - MISMATCHES + XTRA "} if ($errorlevel -eq 5) { echo " - Robocopy - OKCOPY + MISMATCHES "} if ($errorlevel -eq 4) { echo " - Robocopy - MISMATCHES "} if ($errorlevel -eq 3) { echo " - Robocopy - OKCOPY + XTRA "} if ($errorlevel -eq 2) { echo " - Robocopy - XTRA "} if ($errorlevel -eq 1) { echo " - Robocopy - OKCOPY "} if ($errorlevel -eq 0) { echo " - Robocopy - No Change "} }
Wednesday, August 17, 2011
test for files or directories exceeding Windows MAX_PATH (v2)
Oops, tested the previous script on a samba server. For some reason, testing the script on a Windows 2008 R2 domain resulted in an exception. So here“s the new script.
Check will output warnings in red to your screen, all the rest of the data will go to the logfile.
For best results, export to a .csv and open in excel. Then sort the first column.
Calling the script:
path_depth_analysis "G:mydirectory" >c:output.csv
The script:
function path_depth_analysis( $path ) { $items = get-childitem $path if (!($items.count) -eq 0) { foreach ($item in $items) { [int]$length_path = $path.length [int]$length_item = $item.name.length [int]$total_length = $length_path + $length_item if ($total_length -gt 240) { $item_name = $item.name write-host "! - $total_length - $path -> $item_name" -foreground RED } [string]$fullname = $item.FullName [string]$type = $item.GetType().Name if ($type -eq "FileInfo") { echo "$total_length;file;$fullname" } else { echo "$total_length;dir;$fullname" path_depth_analysis "$fullname" } } } }
Friday, August 12, 2011
test for files or directories exceeding Windows MAX_PATH
this script doesn’t seem to work correctly in a Windows-Windows environment, please go to test for files or directories exceeding Windows MAX_PATH (v2)
This week i was reading about a customer that needed an analysis of files or directories that were “too long”. As you may or may not know: if the full path to a file exceeds 260 characters, you may be running into troubles, as Windows does not handle that particularly well.
Microsoft’s article on that: http://msdn.microsof … 365247(v=vs.85).aspx
So i was thinking, how hard can that be? Let’s start powershell and write down a couple of lines …..
$maxpath=260 function testmaxpath($source) { $found_yet=0 $items = get-childitem $source -recurse foreach ($item in $items) { $the_full_name = $item.fullname $the_length = [string]$item.fullname.length if ([int]$the_length -ge $maxpath) { write-host "$the_length $the_full_name" -foregroundcolor red $found_yet++ } } echo "-----------------------------------" echo " found $found_yet files/directories" echo "-----------------------------------" }
then just run it against a disk or share, e.g.
testmaxpath e:\data or testmaxpath \\192.168.1.5\share_x
That’ll give you a nice overview.
off topic:
if you really want to bug your system administrator, he’ll like this:
mkdir \\server\share\%username%\aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbccccccccccccccccccddddddddddddddeeeeeeeeeeeeeffffffffffffffffgggggggggggggggghhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiijjjjjjjjjjjjjjkkkkkkkkkkkkkkkklllllllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnn subst h: \\server\share\%username%\aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbccccccccccccccccccddddddddddddddeeeeeeeeeeeeeffffffffffffffffgggggggggggggggghhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiijjjjjjjjjjjjjjkkkkkkkkkkkkkkkklllllllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnn mkdir h:\aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbccccccccccccccccccddddddddddddddeeeeeeeeeeeeeffffffffffffffffgggggggggggggggghhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiijjjjjjjjjjjjjjkkkkkkkkkkkkkkkklllllllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnn subst i: h:\aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbccccccccccccccccccddddddddddddddeeeeeeeeeeeeeffffffffffffffffgggggggggggggggghhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiijjjjjjjjjjjjjjkkkkkkkkkkkkkkkklllllllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnn mkdir i:\aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbccccccccccccccccccddddddddddddddeeeeeeeeeeeeeffffffffffffffffgggggggggggggggghhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiijjjjjjjjjjjjjjkkkkkkkkkkkkkkkklllllllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnn subst j: i:\aaaaaaaaaaaaabbbbbbbbbbbbbbbbbbccccccccccccccccccddddddddddddddeeeeeeeeeeeeeffffffffffffffffgggggggggggggggghhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiijjjjjjjjjjjjjjkkkkkkkkkkkkkkkklllllllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnn
… and copy some files and set some weird acl’s on them.
Guess what will happen when he wants to delete those directories?
Oh boy, those were the days :)
Friday, March 18, 2011
powershell - set disk quota using wmi
The 2003 powershell goes on :-)
Again, there are no available modules so i had to create my own.
How to set disk quota for a certain user (in megabytes) on a certain Windows 2003 server on a certain drive? Here’s how:
function set_disk_quota($username, $quota_hard, $computername, $disk) { # preferred quota mode is enabled+deny access to disk, which is type 2 # for logging purposes only but still allow disk access, select type 1 $default_quota_mode = "2" $query_quota_enabled_or_not = "select * from Win32_QuotaSetting where VolumePath='"+$disk+":\\'" $query_user = "select * from Win32_Account where name='"+$username+"'" $query_disk = "select * from Win32_LogicalDisk where DeviceID='"+$disk+":'" $quota_disk = get-wmiobject -query $query_quota_enabled_or_not -computername $computername if ($quota_disk.State -eq "0") { echo "CHECK - ERROR - state 0 = Quota not enabled on disk -$disk- of -$computername-" echo "setting quota" $quota_disk.State = $default_quota_mode $quota_disk.Put() } if ($quota_disk.State -eq "1") { echo "CHECK - WARNING - state 1 = Quota enabled on disk -$disk- of -$computername- but not access is not denied when over quota" echo "setting quota" $quota_disk.State = $default_quota_mode $quota_disk.Put() } if ($quota_disk.State -eq "2") { echo "CHECK - OK - state 2 = Quota enabled on disk -$disk- of -$computername- and access is denied when over quota" } $objAccount = get-wmiobject -query $query_user $objDisk = get-wmiobject -query $query_disk $objquota = (new-object management.managementclass Win32_DiskQuota).CreateInstance() $objquota.User = $objAccount.Path.RelativePath $objquota.QuotaVolume = $objDisk.Path.RelativePath if ($quota_hard -eq "0") { $objquota.Delete() echo "Quota deleted for $username" } else { $objquota.Limit = [int]$quota_hard * 1024 * 1024 * 1 # Set the warning level to 90% of the $hard_limit $objquota.WarningLimit = [int]$quota_hard * 1024 * 1024 * 0.9 $objquota.put() } }