{"id":54513,"date":"2009-01-28T21:01:00","date_gmt":"2009-01-28T21:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/01\/28\/hey-scripting-guy-how-can-i-notify-users-of-the-space-their-deleted-items-folder-is-using\/"},"modified":"2009-01-28T21:01:00","modified_gmt":"2009-01-28T21:01:00","slug":"hey-scripting-guy-how-can-i-notify-users-of-the-space-their-deleted-items-folder-is-using","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-notify-users-of-the-space-their-deleted-items-folder-is-using\/","title":{"rendered":"Hey, Scripting Guy! How Can I Notify Users of the Space Their Deleted Items Folder Is Using?"},"content":{"rendered":"<h2><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" height=\"34\" alt=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\"> <\/h2>\n<p>Hey, Scripting Guy! My users seem to think that once they delete an e-mail message, it is gone. Most do not have the concept of going and emptying their Deleted Items folder. Using Windows PowerShell on Exchange, I can easily obtain a report from the server about how much space this stuff is taking up. I would like a script the users can run that would tell them how much space they are using. We have strict quotas set on their mailboxes, and I would like to notify them, perhaps via logon script, of the space used in their Deleted Items folder. I do not want to just arbitrarily empty the contents of that folder because some of the users actually use their deleted items folder for storage and often refer back to those deleted e-mail messages. Can you help me?<\/p>\n<p>&#8211; JH<\/p>\n<p><img decoding=\"async\" height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\"><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" height=\"34\" alt=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\"><\/p>\n<p>Hi JH,<\/p>\n<p>Of course we can help you. The Deleted Items folder is just a folder, like any other Office Outlook folder. In fact, here is <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/jan06\/hey0116.mspx\" target=\"_blank\">a VBScript article<\/a> that talks about obtaining the size and number of items in a folder. Careful examination of that article will reveal several points of similarity between it and today\u2019s script, <b>ReportSpaceUsedByDeletedItems.ps1<\/b> script.<\/p>\n<table class=\"dataTable\" id=\"E6C\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p>This week we are looking at scripting Office Outlook. Interestingly enough, the Office Outlook object model is not as rich as you might suspect. Certainly Office Word and Office Excel have far more capability for automation than Office Outlook. This having been said, a basic familiarity with Office Outlook automation can lend rich results for the enterprising network administrator, consultant, or power user. <\/p>\n<p>If you are interested in VBScript examples of working with Office Outlook, start with the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/all.mspx\" target=\"_blank\">&#8220;Hey, Scripting Guy!&#8221; archive<\/a>, and then move on to the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/officetips\/archive.mspx\" target=\"_blank\">Office Space archive<\/a>. Finally, you would probably like to see some examples of scripts for automating Office Outlook. You are in luck here because we have dozens of well-written scripts in the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/all.mspx\" target=\"_blank\">Community-Submitted Scripts Center<\/a>. Read on, this is going to be a cool week. If you need help with Windows PowerShell, you can find download links and getting started information in the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/msh.mspx\" target=\"_blank\">Windows PowerShell technology hub<\/a>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>In the <b>ReportSpaceUsedByDeletedItems.ps1<\/b> script, we create an instance of the <b>Outlook.Application<\/b> object, make a collection that points to the Deleted Items folder, and walk through the collection of items while adding up their size. Here is today\u2019s script: <\/p>\n<p><b>ReportSpaceUsedByDeletedItems.ps1<\/b><\/p>\n<pre class=\"codeSample\">[Reflection.Assembly]::LoadWithPartialname(\"Microsoft.Office.Interop.Outlook\") |\nout-null\n$i = $size = $null\n$olFolders = \"Microsoft.Office.Interop.Outlook.OlDefaultFolders\" -as [type]\n$outlook = new-object -comobject outlook.application\n$namespace = $outlook.GetNameSpace(\"MAPI\")\n$folder = $namespace.getDefaultFolder($olFolders::olFolderDeletedItems)\n$items = $folder.items\nWrite-Host -ForeGroundColor Green \"There are $($items.count) items in the deleted items folder\"\n$folder.items |\nForeach-Object {\n Write-Progress -Activity \"Talleying size of Deleted items\" -Status \"Adding them up ...\" -PercentComplete ($i\/$items.count*100)\n $size += $_.size\n $i ++\n}\n$size = \"{0:N2}\" -f ($size\/1024)\nWrite-Host -foregroundcolor Red \"The deleted items are consuming $Size kilobytes of space\"\n<\/pre>\n<p>The first thing we need to do is to load the Office Outlook interop assembly. This will give us the ability to load the Office Outlook folder enumerations for the default folder names. It will also give us the ability to use other things from the Office Outlook automation model. To load the <b>Microsoft.Office.Interop.Outlook<\/b> assembly, we use the static <b>LoadWithPartialName<\/b> method from the <b>reflection.assembly<\/b> .NET Framework class as seen here (in Windows PowerShell 2.0 we will be able to use a cmdlet to load the assembly, and the process will be a bit less cryptic if not actually easier):<\/p>\n<pre class=\"codeSample\">[Reflection.Assembly]::LoadWithPartialname(\"Microsoft.Office.Interop.Outlook\") |\nout-null\n<\/pre>\n<p>Now we need to initialize variables. There are actually three variables here. The first two are user defined, <b>$i<\/b> and <b>$size<\/b>. We are saying we want them to be equal to the same thing the <b>$null<\/b> automatic variable is equal to, which of course is null. This is a nice tight syntax that makes it easy to initialize our variables. As a best practice for Windows PowerShell (and even for VBScript), one should always initialize variables before using them. In this way, we know exactly what the value of the variable is when the script runs:<\/p>\n<pre class=\"codeSample\">$i = $size = $null<\/pre>\n<p>When we are done initializing our variables, we want to load the <b>olDefaultFolders<\/b> enumeration. The <b>olDefaultFolders<\/b> enumeration is <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb208072.aspx\" target=\"_blank\">documented on MSDN<\/a>. To load the <b>olDefaultFolders<\/b> enumeration, we place the enumeration name in quotation marks (making it a string) and then we use the <b>\u2013as<\/b> operator to cast the string as a type. This sounds much worse than it is. Here is the code:<\/p>\n<pre class=\"codeSample\">$olFolders = \"Microsoft.Office.Interop.Outlook.OlDefaultFolders\" -as [type]<\/pre>\n<p>Then we create an instance of the <b>Outlook.Application<\/b> object. The <b>Outlook.Application<\/b> object is the main object to use when working with Office Outlook automation. The <b>Outlook.Application<\/b> object is <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb219938.aspx\" target=\"_blank\">documented on MSDN<\/a>. To create the object, we use the <b>New-Object<\/b> cmdlet with the <b>comobject<\/b> parameter. The program ID is <b>Outlook.Application<\/b>. We store the returned object in the <b>$outlook<\/b> variable as seen here: <\/p>\n<pre class=\"codeSample\">$outlook = new-object -comobject outlook.application<\/pre>\n<p>It is time to create the <b>namespace<\/b> object. To do this, we use the <b>GetNameSpace<\/b> method. We tell it to use MAPI because this is the only constructor that will work for this method. And because the <b>GetNameSpace<\/b> method is a method, we need to use parentheses when we are passing a value to the method. All methods in Windows PowerShell use parentheses. This is seen here:<\/p>\n<pre class=\"codeSample\">$namespace = $outlook.GetNameSpace(\"MAPI\")<\/pre>\n<p>Now we want to connect to the Deleted Items folder. To connect to a folder, we use the <b>GetDefaultFolder<\/b> method and give it one of the <b>olDefaultFolders<\/b> enumeration values. If you are unsure which enumeration values are allowed, you can either look them up on MSDN or use the technique we talked about in <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/jan09\/hey0127.mspx\" target=\"_blank\">yesterday&#8217;s article<\/a>. When using one of the <b>olDefaultFolders<\/b> enumeration values, we use double colons to separate the variable containing the enumeration type from the name of the enumeration (think about accessing a static property):<\/p>\n<pre class=\"codeSample\">$folder = $namespace.getDefaultFolder($olFolders::olFolderDeletedItems)<\/pre>\n<p>Then we use the <b>items<\/b> property from the <b>folder<\/b> object to obtain a collection of items from the Deleted Items folder: <\/p>\n<pre class=\"codeSample\">$items = $folder.items<\/pre>\n<p>We now print out a status message that tells the user how many items are in the Deleted Items folder. This is easy to do and happens nearly immediately. In this line of code, we are using the <b>Write-Host<\/b> cmdlet to print to the screen. Normally, we do not need to use <b>Write-Host<\/b> cmdlet, but we want the text to appear in green. We can use any color that is documented in the <b>System.ConsoleColor<\/b> enumeration class. If you do not want to look the enumeration up on MSDN, you can use the <b>GetNames<\/b> static method from the <b>System.Enum<\/b> .NET Framework class. This is seen&nbsp;here:<\/p>\n<pre class=\"codeSample\">PS C:\\&gt; [enum]::GetNames(\"System.ConsoleColor\")\nBlack\nDarkBlue\nDarkGreen\nDarkCyan\nDarkRed\nDarkMagenta\nDarkYellow\nGray\nDarkGray\nBlue\nGreen\nCyan\nRed\nMagenta\nYellow\nWhite\n<\/pre>\n<p>The other thing we do is print out the number of items in the <b>items<\/b> collection. To do this, we need to evaluate the <b>count<\/b> property. To force the <b>count<\/b> property to be evaluated prior to returning the number of items, we use the subexpression <b>$() <\/b>to surround the property (if we did not use the subexpression, the object would unravel inside our string and fill the screen with <a href=\"http:\/\/en.wikipedia.org\/wiki\/Gobbledygook\" target=\"_blank\">gobbledygook<\/a>): <\/p>\n<pre class=\"codeSample\">Write-Host -ForeGroundColor Green \"There are $($items.count) items in the deleted items folder\"<\/pre>\n<p>Next we want to add up the size of all the messages in the Deleted Items folder. This will take a bit of time, but we can use pipelining to help it to go more efficiently. We take the <b>items<\/b> collection and pipeline it to the next part of the code by using the pipeline symbol (&#8220;|&#8221;):<\/p>\n<pre class=\"codeSample\">$folder.items |<\/pre>\n<p>We pipeline the collection of items to the <b>ForEach-Object<\/b> cmdlet to allow us to work with individual items from the collection:<\/p>\n<pre class=\"codeSample\">ForEach-Object {<\/pre>\n<p>As this operation is happening, we use the <b>Write-Progress<\/b> cmdlet to display feedback to the user. Depending on the number of deleted items, the operation could take a substantial amount of time. We specify three parameters for the <b>Write-Progress<\/b> cmdlet: the <b>activity<\/b>, the <b>status<\/b>, and the <b>percentcomplete<\/b>. This is seen here:<\/p>\n<pre class=\"codeSample\">Write-Progress -Activity \"Talleying size of Deleted items\" -Status \"Adding them up ...\" -PercentComplete ($i\/$items.count*100)<\/pre>\n<p>We now want to maintain a running tally of the size of the deleted items. To do this, we use the <b>size<\/b> property of the object and add it to the <b>$size<\/b> variable. To take the current value and add the new value to it, we use the <b>+=<\/b> operator: <\/p>\n<pre class=\"codeSample\"> $size += $_.size<\/pre>\n<p>We increment the <b>$i<\/b> variable because it is the one we are using to track our progress and to provide feedback to the user via the <b>Write-Progress<\/b> cmdlet: <\/p>\n<pre class=\"codeSample\"> $i ++\n}\n<\/pre>\n<p>Now want to format the number that is displayed. To format the number, we use the .NET Framework standard format strings which are <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dwhawy9k(VS.80).aspx\" target=\"_blank\">documented on MSDN<\/a>. In our example, <b>N<\/b> means we are formatting a number; the <b>2<\/b> means we will use two decimal places; and the <b>0<\/b> means to format it with the default pattern: <\/p>\n<pre class=\"codeSample\">$size = \"{0:N2}\" -f ($size\/1024)<\/pre>\n<p>The last thing we need to do is to report the total size of deleted items. We once again use the <b>Write-Host<\/b> cmdlet and this time we make the color of the text red. Because the <b>$size<\/b> variable is a simple variable, and not an object or a method or property that needs to be evaluated, we do not need to use a subexpression when printing out the value. This is seen here: <\/p>\n<pre class=\"codeSample\">Write-Host -foregroundcolor Red \"The deleted items are consuming $Size kilobytes of space\"<\/pre>\n<p>The result from running the <b>ReportSpaceUsedByDeletedItems.ps1<\/b> script is seen here:<\/p>\n<p><img decoding=\"async\" height=\"382\" alt=\"Image of the result of running the script\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/january\/hey0128\/hsg-1-28-09-01.jpg\" width=\"500\" border=\"0\"><\/p>\n<p>&nbsp;<\/p>\n<p>Well, JH, that is it for the <b>ReportSpaceUsedByDeletedItems.ps1<\/b> script. I hope you have enjoyed the article and that you will find the script useful. Stay tuned tomorrow, when we continue with Office Outlook Week. See you then.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! My users seem to think that once they delete an e-mail message, it is gone. Most do not have the concept of going and emptying their Deleted Items folder. Using Windows PowerShell on Exchange, I can easily obtain a report from the server about how much space this stuff is taking up. [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[212,49,3,45],"class_list":["post-54513","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-microsoft-outlook","tag-office","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! My users seem to think that once they delete an e-mail message, it is gone. Most do not have the concept of going and emptying their Deleted Items folder. Using Windows PowerShell on Exchange, I can easily obtain a report from the server about how much space this stuff is taking up. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54513","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=54513"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54513\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=54513"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=54513"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=54513"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}