30 Mar 2009 @ 16:17 

A friend of mine called me, and asked me for help… He had had a migration go wrong and had ended up with a bunch of .pst files containing 1000′s of emtpy folders. He had started cleaning up the .pst’s manually, after having spent several hours on one .pst file he decided on another approach, and this is where I came in.

He asked if it was possible to write a script that would delete all the empty folders for him, I said to him that I bet it is possible, just not sure if I can do it, I have never really “scripted” Outlook before..

So I started looking for ways to manipulate data in Outlook and found out that the Outlook.Application COM object allowed me to manipulate folders and messages within Outlook.. Did some quick testing, told my friend that it should be possible, and that I would get back to him…

After some mucking around here is what I ended up with.

$olApp = new-object -com Outlook.Application
$namespace = $olApp.GetNamespace("MAPI")
#Root folder to match, look in your outlook and look at what folders you have.
#RooFolders could be Mailbox - <username>, Public folders, or other mapped in PST's or mailboxes.
$RootfolderToMatch = "Inbox"

$Global:StopValue = 1

Function ListEmpty {
Param ($infolder)
  Foreach ($fldr in $infolder.Folders) {

    If ($fldr.Items.Count -eq 0 -and $fldr.Folders.Count -eq 0) {

	  # I have had to check for system folders, these cannot be deleted via the script, so to prevent the script for erroring out, I check for it.
    if ($fldr.name -match "Slettet" -or $fldr.name -match "Deleted" -or $fldr.name -match "journal" -or $fldr.name -match "rss"`
	    -or $fldr.name -match "note" -or $fldr.name -match "kalender" -or $fldr.name -match "kontakt" -or $fldr.name -match "udbakke" `
		-or $fldr.name -match "Sync" -or $fldr.name -match "Server" -or $fldr.name -match "contacts" -or $fldr.name -match "Outbox" )
      {"Cannot delete systemfolder :  """ + $fldr.FolderPath + ""

	  }

    Else {
      if ($fldr.Parent.name -notmatch "slettet" -and $fldr.Parent.name -notmatch "Deleted" ) {
	  #Remove # from the below line, to actually delete Outlook folders.
      #$fldr.delete()
      $fldr.FolderPath + " has been deleted!!"

	  $Global:StopValue = 0
	  }
         }
 }

    Else {
      if ($fldr.name.length -gt 0) {

       ListEmpty($fldr)}
	   }

 }

}

$RootFolders = $namespace.Folders  | ?{$_.name -match $RootfolderToMatch}

Listempty($RootFolders)

While ($Global:StopValue -eq 0) {
$Global:StopValue = 1
Listempty($RootFolders)
}

I start by initializing the Outlook COM object, and declaring some variables

$olApp = new-object -com Outlook.Application
$namespace = $olApp.GetNamespace("MAPI")
#Root folder to match, look in your outlook and look at what folders you have.
#RooFolders could be Mailbox - </username><username>, Public folders, or other mapped in PST's or mailboxes.
$RootfolderToMatch = "Inbox"

$Global:StopValue = 1

I then create a function that takes an Outlook folder as a parameter.
I then loop through each subfolder, and checks to see if each folder contains any files or other folders, if it finds a folder that does not contain any files/folders it then checks to see if the folder name is something like “outbox, sent items, Journal etc etc.” The reason I do this is because these are system folders, and cannot be deleted, so if the script tried to delete one of them, it would come back with an error.

So if the folder that is currently being checked is empty and is not a system folder it will be deleted.(Actually the line where the folder is deleted is commented out, just to make sure there is not accidental deletions) The script then recurses through all subfolders to check for empty folders.


Function ListEmpty {
Param ($infolder)
  Foreach ($fldr in $infolder.Folders) {

    If ($fldr.Items.Count -eq 0 -and $fldr.Folders.Count -eq 0) {

	  # I have had to check for system folders, these cannot be deleted via the script, so to prevent the script for erroring out, I check for it.
    if ($fldr.name -match "Slettet" -or $fldr.name -match "Deleted" -or $fldr.name -match "journal" -or $fldr.name -match "rss"`
	    -or $fldr.name -match "note" -or $fldr.name -match "kalender" -or $fldr.name -match "kontakt" -or $fldr.name -match "udbakke" `
		-or $fldr.name -match "Sync" -or $fldr.name -match "Server" -or $fldr.name -match "contacts" -or $fldr.name -match "Outbox" )
      {"Cannot delete systemfolder :  """ + $fldr.FolderPath + ""

	  }

    Else {
      if ($fldr.Parent.name -notmatch "slettet" -and $fldr.Parent.name -notmatch "Deleted" ) {
	  #Remove # from the below line, to actually delete Outlook folders.
      #$fldr.delete()
      $fldr.FolderPath + " has been deleted!!"

	  $Global:StopValue = 0
	  }
         }
 }

    Else {
      if ($fldr.name.length -gt 0) {

       ListEmpty($fldr)}
	   }

 }

}

Rootfolders is the initial “path”/folder that the scripts starts to look for empty folders from.
$namespace.folders is the highest folder level in Outlook, it contains the user mailbox, public folders and any mapped in .pst files, so what I do here is match a single top level folder.

Here is an example, lets say I have opened a .pst file called BusinessCases09, I would set $RootfolderToMatch equal to BusinessCases09 (This is set at the beginning of the script), that means when the ListEmpty function is called for the first time, it will scan through all subfolders of the .pst file.


$RootFolders = $namespace.Folders  | ?{$_.name -match $RootfolderToMatch}

Listempty($RootFolders)

In order to make sure the script runs to all empty folders is deleted, I have set up a while loop to call function until no more empty folders are found.

While ($Global:StopValue -eq 0) {
$Global:StopValue = 1
Listempty($RootFolders)

Posted By: Xenophane
Last Edit: 30 Mar 2009 @ 16:17

EmailPermalinkComments (1)
Tags
Tags: , , , ,
Categories: Everyday
 11 Mar 2009 @ 20:25 

My CEO asked me for a report about mailbox usage in the company I work for, so I thought this has to be a job for powershell.

So I sat down and tried to figure out how to get info from Exchange, I know there is a WMI class that I can query about information about the different mailboxes, so I thought I would give it a try to write a script to output things like Number of items in each mailbox, the size, deleted size etc.
I stumbled on a script by Michael B. Smith, who had made a similar script, and really done some nice work on the output of the script, so I decided not to reinvent the wheel and use his formatting.

# Get-MailBoxSize.ps1
# Claus T N 2009
# This script connects to an Exchange server(s) and returns all mailbox sizes
# number of items, plus a summary.
# All formatting is "stolen" from a script by Michael B. Smith

$day = Get-Date -UFormat "%Y%m%d"
#File to output results to
$OutPutFile = "c:\scripts\MailBoxInfo-$day.txt"

[System.Int64]$script:totalSize      = 0
[System.Int64]$script:totalItems     = 0
[System.Int64]$script:totalDeleted   = 0
[System.Int64]$script:totalMailboxes = 0

# Gets data through WMI from specified Exchange mailbox servers
$ExchangeServers = "dksrv01"

Function Get-MailboxInfo {
foreach ($computer in $ExchangeServers) {
$Mailboxes = Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object Size `

				Foreach ($mailbox in $mailboxes){
				If ($mailbox.MailBoxDisplayName -like "SMTP (*" -or $mailbox.MailBoxDisplayName -like "SystemMailbox*"){
				}
				else {
				[string]$str = ([string]$Mailbox.MailboxDisplayname).PadRight(40)
                $str += ([System.Int64]$Mailbox.Size).ToString("N0").PadLeft(12)
                $str += " KB"
                $str += ([System.Int32]$MailBox.TotalItems).ToString("N0").PadLeft(9)
                $str += "   "
                $str += ([System.Int64]$Mailbox.DeletedMessageSizeExtended).ToString("N0").PadLeft(12)
                $str += " KB"
                $str | Out-File $OutPutFile -append
				#"----------------------------------------------------------------------------------" | Out-File $OutPutFile -append

                $script:totalSize += ([System.Int64]$mailbox.Size)
                $script:totalItems += ([System.Int32]$mailbox.TotalItems)
                $script:totalDeleted += ([System.Int64]$mailbox.DeletedMessageSizeExtended)
                $script:totalMailboxes += 1
				}
}
}
}

"Mailbox name                               Mailbox size  Item count   Deleted size" | Out-File $OutPutFile -append
"==================================================================================" | Out-File $OutPutFile -append
Get-MailBoxInfo

"==================================================================================" | Out-File $OutPutFile -append
$string =  ($script:totalMailboxes.ToString("N0") + " mailboxes").PadRight(40)
$string += $script:totalSize.ToString("N0").PadLeft(12) + " KB"
$string += $script:totalItems.ToString("N0").PadLeft(9) + "   "
$string += $script:totalDeleted.ToString("N0").PadLeft(12) + " KB"
$string | Out-File $OutPutFile -append
"==================================================================================" | Out-File $OutPutFile -append
Posted By: Xenophane
Last Edit: 11 Mar 2009 @ 20:25

EmailPermalinkComments (1)
Tags
Categories: Everyday
 11 Mar 2009 @ 19:56 

Since I once in a while post powershell scripts on my site, I like that fact that I can have them color highlighted for better readability, so I played around with several different syntax highlighters, but none of them was able to do powershell, so I created a brush for Syntax highligther plus.

This is my initial attempt, updated to work with Syntax Highlighter plus 2

#
#  Author: Claus T Nielsen
#  http://www.xipher.dk
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, version 3 of the License.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http ://www.gnu.org/licenses/>.

SyntaxHighlighter.Brushes.PoSh = function()
{
    var builtins =  'ForEach-Object Format-Custom Format-List Format-Table Format-Wide Get-Acl Get-Alias Get-AuthenticodeSignature Get-ChildItem ' +
                    'Disable-PSBreakpoint Disable-QADUser Disconnect-EventListener Disconnect-QADService Enable-PSBreakpoint Enable-QADUser Export-Alias Export-Clixml Export-Console ' +
                    'Get-Content Get-Credential Get-Culture Get-Date Get-Event Get-EventBinding Get-EventLog Get-ExecutionPolicy Get-Help ' +
                    'Get-PSJob Get-PSProvider Get-PSSnapin Get-QADComputer Get-QADGroup Get-QADGroupMember Get-QADObject Get-QADPasswordSettingsObject Get-QADPSSnapinSettings ' +
					'Get-Runspace Get-Service Get-TraceSource Get-UICulture Get-Unique Get-Variable Get-WmiObject Group-Object Import-Alias ' +
					'Import-Csv Import-LocalizedData Invoke-Expression Invoke-History Invoke-Item Invoke-WmiMethod Join-Path Measure-Command Measure-Object ' +
					'Move-ItemProperty Move-QADObject New-Alias New-Event New-Item New-ItemProperty New-Object New-PSBreakpoint New-PSDrive ' +
					'New-QADObject New-QADPasswordSettingsObject New-QADUser New-Runspace New-Service New-TimeSpan New-Variable Out-Default Out-File ' +
					'Out-Host Out-Null Out-Printer Out-String Pop-Location Push-Location Read-Host Receive-PSJob Remove-Item ' +
					'Remove-PSBreakpoint Remove-PSDrive Remove-PSJob Remove-PSSnapin Remove-QADGroupMember Remove-QADObject Remove-QADPasswordSettingsObjectAppliesTo Remove-Runspace Remove-Variable ' +
					'Rename-Item Rename-ItemProperty Rename-QADObject Resolve-Path Restart-Service Resume-Service Select-Object Select-String Set-Acl ' +
					'Set-AuthenticodeSignature Set-Content Set-Date Set-ExecutionPolicy Set-Item Set-ItemProperty Set-Location Set-PSDebug Set-QADGroup ' +
					'Set-QADPSSnapinSettings Set-QADUser Set-Service Set-TraceSource Set-Variable Set-WmiInstance Sort-Object Split-Path Start-KeyHandler ' +
					'Start-Service Start-Sleep Start-Transcript Step-Into Step-Out Step-Over Stop-KeyHandler Stop-Process Stop-PSJob ' +
					'Stop-Transcript Suspend-Service Tee-Object Test-Path Trace-Command Unlock-QADUser Update-FormatData Update-TypeData Wait-PSJob ' +
					'Write-Debug Write-Error Write-Host Write-Output Write-Progress Write-Verbose Write-Warning ' +
					'suspend test times trap type typeset ulimit umask unalias unset wait';

    var keywords =  'case do done elif elseif esac fi for function if in select then ' +
					'-band -bor -bnot -match -notmatch -like -notlike -eq -ne -gt -ge -lt -le -is ' +
                    '-imatch -inotmatch -ilike -inotlike -ieq -ine -igt -ige -ilt -ile ' +
					'time until while filter foreach not ft sort';

    this.regexList = [
                    /* comments */
                    {regex: SyntaxHighlighter.RegexLib.SingleLinePerlComments, css: 'comment'},
                    /* text qualifiers */
                    {regex: SyntaxHighlighter.RegexLib.DoubleQuotedString, css: 'string'},
                    {regex: SyntaxHighlighter.RegexLib.SingleQuotedString, css: 'string'},
                    /* deliminators */
                    {regex: new RegExp('[()[\\]{}]', 'g'), css: 'delim'},
                    /* variables */
                    {regex: new RegExp('\\$\\w+', 'g'), css: 'vars'},
                    {regex: new RegExp('\\w+=', 'g'), css: 'vars'},
                    /* flags */
                    {regex: new RegExp('\\s-\\w+', 'g'), css: 'test'},
                    /* builtins */
                    {regex: new RegExp(this.GetKeywords(builtins), 'igm'), css: 'builtin'},
                    /* keywords */
                    {regex: new RegExp(this.GetKeywords(keywords), 'igm'), css: 'keyword'}
                    ];

  };

SyntaxHighlighter.Brushes.PoSh.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.Brushes.PoSh.Aliases = ['PoSh', 'ps'];

I modified a version made by Joel Bennet to work with version 2 as well.


/*Updated to v2 by Claus T Nielsen (Xipher.dk */
/* Powershell 1.0 syntax contributed by Joel Bennett */
SyntaxHighlighter.brushes.PowerShell = function()
{
	var keywords =	'while until trap switch return ref process param in if global: function foreach for finally filter end elseif else do default continue break begin \\? % #script #private #local #global';

   var operators = 'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle clike clt cmatch cne cnotcontains cnotlike cnotmatch contains creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt imatch ine inotcontains inotlike inotmatch ireplace is isnot le like lt match ne not notcontains notlike notmatch or regex replace wildcard';

   var verbs = 'Write Wrap Width Where Whatif Warning Wait Version Verify Verbose Value UserName URL Update Unlock Unique Uninstall TypeName TrustLevel Trusted Truncate Trace TimeStamp TimeOut TID Test Temp Tee Suspend Strict Stop Statistic State Start Split Speed SortBy Sort Size SID Shortname Set Send Select Scope SaveCred Role Retry Resume Restore Restart Resolve Resize Repair Rename Remove Regex Recurse Receive Reason ReadOnly Read Push Property Prompt Privilege Priority PrinterName PrincipalName PortName Pop Ping Password PassThru ParentID Parameter Owner Overwrite Output Out Operation Notify NewLine New Name Move Most Modified Minimum Migrate Merge Measure Maximum MacName LogName Log Lock Location LineCount Limit KeyLength KeyContainerName KeyAlgorithm Join JobName IpAddress Invoke Interval InterfaceName Interactive Install Insert Input Initialize Incremental Include Import Ignore Id Group Get From Format ForEach Force Follow Filter FileName Fast Export Exclude Exact EventName ErrorLimit ErrorLevel Erase Encrypt Encoding Enable Elapsed DriveName Drain DomainName Disconnect Disable DirectoryName Description Descending Delete Default Debug Data Verbs CSPType CSPName Created Create Count Copy ConvertTo ConvertFrom Convert Continuous Connect Confirm ComputerName Compress Compatible Compare Command ClusterName Clear ClassName Checkpoint Char CertUsage CertSubjectName CertStoreLocation CertSerialNumber CertRequestFileName CertIssuerName CertFile CaseSensitive Cache BlockCount Binary Before AttributeName AssemblyName Ascending As ApplicationName Allocation All After Add ACL Accessed';

	this.regexList = [
		{ regex: SyntaxHighlighter.regexLib.singleLinePerlComments,					   							    css: 'color4' },	// one line comments
		{ regex: new RegExp('@"\\n[\\s\\S]*?\\n"@', 'gm'),			   		   		                                css: 'string' },	// double quoted here-strings
        { regex: new RegExp("@'\\n[\\s\\S]*?\\n'@", 'gm'),			                                                css: 'string' },	// single quoted here-strings
		{ regex: new RegExp('"(?:\\$\\([^\\)]*\\)|[^"]|`")*[^`]"','g'),						   		                css: 'string' },	// double quoted strings
		{ regex: new RegExp("'[^']*'", 'g'),						   		                                        css: 'string' },	// single quoted strings
		{ regex: new RegExp('(?:'+verbs.replace(/ /g, '\\b|\\b')+')-[a-zA-Z_][a-zA-Z0-9_]*', 'gmi'),	            css: 'cmdlet' },	// cmdlets
		{ regex: new RegExp('\\[[A-Z_\\[][A-Z0-9_. ,\\[\\]]*\\]*', 'gi'),	                                        css: 'color2' },	   // .Net [Type]s
		{ regex: new RegExp('\\$(?:(?:global|script|private|env):)?[A-Z0-9_]+', 'gi'),	                            css: 'variable' },// $variables
		{ regex: new RegExp(this.getKeywords(keywords), 'gmi'),	                                                    css: 'keyword' },	// keywords
		{ regex: new RegExp('-'+this.getKeywords(operators), 'gmi'),                                                css: 'constants' },	// operators
		{ regex: new RegExp('\\s+-[a-zA-Z_][a-zA-Z0-9_]*', 'gmi'),                                                  css: 'color1' }	// operators
		]

};

SyntaxHighlighter.brushes.PowerShell.prototype	= new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.PowerShell.aliases	= ['powershell', 'posh', 'ps'];

Be aware that compared to version 1 of the brushes you can no longer have specific css settings in the brush (previously you could define font/colors for different datatypes/keywords in the brush), this has been changed inversion 2 because of a new theme options.

Posted By: Xenophane
Last Edit: 11 Mar 2009 @ 20:07

EmailPermalinkComments (2)
Tags
Categories: Tech Stuff
 01 Mar 2009 @ 22:12 

One of the guys at the minasi forum had a problem with setting permissions on multiple .ini files (Had to add the Deny setting to read for a certain group), he joked that I was probably able to write a powershell script to do it.. So I took the challenge, and wrote this little script.

This script will lrecurse through the InitialFolder and all of its subfolders for Desktop.Ini files, it will then loop through each of the files, and add the DENY setting on the read permission for the local administrators group.

$InitialFolder = "c:\scripts\test\"

$Files = Get-ChildItem $InitialFolder -include desktop.ini -recurse -force

Foreach ($File in $Files) {
$colRights = [System.Security.AccessControl.FileSystemRights]"Read" 

$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None 

$objType =[System.Security.AccessControl.AccessControlType]::DENY 

$objUser = New-Object System.Security.Principal.NTAccount("Administrators") 

$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ($objUser, $colRights, $InheritanceFlag, $PropagationFlag,$objType) 

$objACL = Get-ACL $File
$objACL.AddAccessRule($objACE) 

Set-ACL $file $objACL
}
Posted By: Xenophane
Last Edit: 10 Mar 2009 @ 22:16

EmailPermalinkComments (2)
Tags
Categories: Tech Stuff

 Last 50 Posts
 Back
Change Theme...
  • Users » 159
  • Posts/Pages » 101
  • Comments » 73
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

Links



    No Child Pages.

Books



    No Child Pages.

Ønsker



    No Child Pages.

CV



    No Child Pages.