<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>sidefumbling &#187; Exchange 2007</title>
	<atom:link href="http://www.flobee.net/category/exchange/exchange-2007/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.flobee.net</link>
	<description>The consequence of not having six hydrocoptic marzelvanes.</description>
	<lastBuildDate>Fri, 20 Jan 2012 17:04:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>How to clear the mail attribute using PowerShell</title>
		<link>http://www.flobee.net/how-to-clear-the-mail-attribute-using-powershell/</link>
		<comments>http://www.flobee.net/how-to-clear-the-mail-attribute-using-powershell/#comments</comments>
		<pubDate>Mon, 09 Nov 2009 16:09:58 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=409</guid>
		<description><![CDATA[I have been struggling to delete the value in the mail attribute after a mailbox has been deleted. Exchange populates the mail attribute when a mailbox is created (even though Exchange has no use for the attribute), but doesn&#8217;t clear the attribute when the mailbox is deleted. With ADUC integration removed in Exchange 2007, a [...]]]></description>
			<content:encoded><![CDATA[<p>I have been struggling to delete the value in the mail attribute after a mailbox has been deleted.  Exchange populates the mail attribute when a mailbox is created (even though Exchange has no use for the attribute), but doesn&#8217;t clear the attribute when the mailbox is deleted.  With ADUC integration removed in Exchange 2007, a quick way to know if an account has a mailbox is to look at the mail attribute.  But if removing a mailbox no longer clears that attribute, it is difficult know (just by looking at a user account in ADUC) if the account still has a mailbox.</p>
<p>Since Exchange doesn&#8217;t use the mail attribute, you can&#8217;t use the <span class="flobeecode" id="codekeyword">Set-Mailbox</span> attribute, especially if the mailbox is deleted anyway.  I tried using <span class="flobeecode" id="codekeyword">Set-User</span> with the <span class="flobeecode" id="codekeyword">-WindowsEmailAddress</span> parameter, but because the data type is <span class="flobeecode" id="codeplain">Microsoft.Exchange.Data.SmtpAddress</span>, setting the value to &quot;&quot; or <span class="flobeecode" id="codevariable">$null</span> doesn&#8217;t work because those aren&#8217;t properly formatted SMTP addresses.</p>
<p>So, I figured I needed to get away from any Exchange cmdlet.  I used PowerShell&#8217;s native support for ADSI to bind to the user object: <span class="flobeecode" id="codekeyword">New-Object</span> <span class="flobeecode" id="codeplain">DirectoryServices.DirectoryEntry</span> <span class="flobeecode" id="codestring">&quot;LDAP://UserDN&quot;</span>.  But you will get an error if you try to set the attribute to null (<span class="flobeecode" id="codevariable">$user</span><span class="flobeecode" id="codeplain">.mail = </span><span class="flobeecode" id="codevariable">$null</span>).  You can set it to an empty value (&quot;&quot;), but you will then get an error when you try to commit the change: <span class="flobeecode" id="codevariable">$user</span><span class="flobeecode" id="codeplain">.SetInfo()</span>.</p>
<p>How can you possibly clear this attribute, one that is so easy to do in ADUC just by deleting the value in it?  It is necessary to fall back to the <span class="flobeecode" id="codeplain">PutEx</span> method.  Using that will let you use the <span class="flobeecode" id="codeconstant">ADS_PROPERTY_CLEAR</span> constant (indicated by the numeric one in the first argument).  It has taken me days to finally get to this point, so hopefully this post will shorten that time for others trying to do the same thing.</p>
<pre class="brush:ps;gutter:false;collapse:false">
$user = Get-User "username"
$ldapDN = "LDAP://" + $user.distinguishedName
$adUser = New-Object DirectoryServices.DirectoryEntry $ldapDN
$adUser.PutEx(1, "mail", $null)
$adUser.SetInfo()
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/how-to-clear-the-mail-attribute-using-powershell/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Add caching to mailbox creation load balancing script</title>
		<link>http://www.flobee.net/add-caching-to-mailbox-creation-load-balancing-script/</link>
		<comments>http://www.flobee.net/add-caching-to-mailbox-creation-load-balancing-script/#comments</comments>
		<pubDate>Tue, 22 Sep 2009 19:57:52 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=135</guid>
		<description><![CDATA[In a previous post I instructed how to load balance new mailboxes across databases. In a large environment, however, this determination can take upwards of 90 seconds. Normally I don&#8217;t run the mailbox creation script at my company (our IT Security department does), but I ran it yesterday and it took 55 seconds to determine [...]]]></description>
			<content:encoded><![CDATA[<p>In a <a href="http://www.flobee.net/load-balance-new-mailbox-creation-across-multiple-databases-and-servers-using-powershell/">previous post</a> I instructed how to load balance new mailboxes across databases.  In a large environment, however, this determination can take upwards of 90 seconds.  Normally I don&#8217;t run the mailbox creation script at my company (our IT Security department does), but I ran it yesterday and it took 55 seconds to determine the database.  That&#8217;s just too long for me.</p>
<p>So I decided to add what was going to be my fallback option in the first place: using a cached list.  This code uses an xml file to keep track of the database to use at a given location and a timestamp of when that entry was last updated.  The xml file uses the following format:</p>
<p>
<pre class="brush:xml;gutter:false;wrap-lines:false;auto-links:false;collapse:false">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;sites&gt;
	&lt;site id=&quot;Name1&quot;&gt;
		&lt;database name=&quot;Server1\Storage Group 1\Mailbox Store 1&quot; /&gt;
		&lt;timestamp time=&quot;2009-09-22T08:59:29.4878574-07:00&quot; /&gt;
	&lt;/site&gt;
	&lt;site id=&quot;Name2&quot;&gt;
		&lt;database name=&quot;Server2\Storage Group 5\Mailbox Store 1&quot; /&gt;
		&lt;timestamp time=&quot;2009-09-22T08:17:51.5632031-07:00&quot; /&gt;
	&lt;/site&gt;
....
&lt;/sites&gt;
</pre>
<p>Set the $sourcexml variable to the full path to the file.  The function that calls this code should set the $Location parameter to whatever value is stored in the id attribute of the site nodes.  The xml file is opened, the timestamp for a given site name is returned, and compared to a time interval (in this case, 24 hours, but you can set it to anything you want).  If less than 24 hours old, the database name in the site node is used.  If more than 24 hours old, it will run the code to determine the database to use (from the other post).  After the determination is made, it writes the database name and the current time back into the xml file and saves it.</p>
<p>The on-demand query for one site ran in 53 seconds through mailbox creation for the first run, but created the second mailbox in 11 seconds.  That is much more tenable, at least for an impatient person like myself.</p>
<pre class="brush:ps;gutter:false;wrap-lines:false;auto-links:false;collapse:false">
$start = Get-Date
$sourcexml = "Path to input xml file.xml"
[xml]$db = Get-Content $sourcexml
$site = $db.sites.site | where {$_.id -eq $Location}
[datetime]$lastUpdate = $site.Timestamp.Time
if (($start - $lastUpdate).TotalHours -lt 24)
	{
	Write-Host "Timstamp for selected site is less than 24 hours old.  Using cached entry."
	$site.database.name
	}
else
	{
	Write-Host "Timestamp for selected site is more than 24 hours old.  Calculating database to use."
	#Run code here to determine database to use
	#
	#When done, continue
	$site.database.name = ($mailboxcount.GetEnumerator() | sort value | select -first 1).key
	$site.timestamp.time = (Get-Date -Format o).ToString()
	$db.save($sourcexml)
	$site.database.name
	}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/add-caching-to-mailbox-creation-load-balancing-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Load balance new mailbox creation across multiple databases and servers using PowerShell</title>
		<link>http://www.flobee.net/load-balance-new-mailbox-creation-across-multiple-databases-and-servers-using-powershell/</link>
		<comments>http://www.flobee.net/load-balance-new-mailbox-creation-across-multiple-databases-and-servers-using-powershell/#comments</comments>
		<pubDate>Wed, 27 May 2009 21:42:29 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=64</guid>
		<description><![CDATA[I needed a way to have new mailboxes be automatically distributed across a list of databases on multiple servers, and I recalled seeing a script somewhere.  The blog post with those details can be found here.  As the author points out, it can take a long time to build the hash table, too long for [...]]]></description>
			<content:encoded><![CDATA[<p>I needed a way to have new mailboxes be automatically distributed across a list of databases on multiple servers, and I recalled seeing a script somewhere.  The blog post with those details can be found <a title="Load balance mailbox creation" href="http://jonoble.spaces.live.com/Blog/cns!CC73D8744F0894A5!629.entry" target="_blank">here</a>.  As the author points out, it can take a long time to build the hash table, too long for my requirements.  For example, one of my longer queries builds the table for 9 databases with over 5000 mailboxes in 58 seconds.  That is just way too  long for someone to feed a user account into to get a mailbox created.</p>
<p>So I broke down the query being used to find where efficiency could be improved.  The Get-ExchangeServer cmdlet slows the script down and can be avoided altogether by feeding the server list directly into the Get-MailboxDatabase cmdlet.  Additionally, I discovered just how inefficient the Exchange cmdlets are.  I already knew they were based on running other cmdlets for targets servers across a WAN that return not a lot of data, but this reinforced those findings.</p>
<p>To get away from the Exchange cmdlets, I query AD directly by instantiating ADSI in .NET manually.  Instead of slowly building the hash table I just take the record count of the search results and populate the table entry at once.  This reduced the time to completion from 58 seconds to 24 seconds.  These findings are consistent across different database counts and mailbox totals: 60% reduction in time.  While still too long for my taste, it certainly is much better than before and is within reason for the people at my company that make use of the script.</p>
<p>Since my script encompasses more than just the load balancing part (I create a GUI form where the location for the mailbox is selected, which feeds a list of appropriate servers to the function), this is just the part that creates the hash table with the database counts and returns the database with the lowest count:</p>
<pre class="brush:ps;gutter:false;wrap-lines:false;auto-links:false;collapse:false">
$mbxServers = @()
$mailboxcount = @{}
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root = [ADSI]"GC://$($domain.Name)"
$mbxServers | %{Get-MailboxDatabase -server $_} | ?{$_.recovery -eq $FALSE} | %{
$filter = "(&amp;(objectcategory=user)(homeMDB=" + ($_.DistinguishedName) + "))"
$search = new-Object System.DirectoryServices.DirectorySearcher($root,$filter)
$search.PropertiesToLoad.Add("homeMDB") | Out-Null
$result = $search.FindAll()
$mailboxcount["$($_.Identity)"] = $result.count
}
($mailboxcount.GetEnumerator() | sort value | select -first 1).key
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/load-balance-new-mailbox-creation-across-multiple-databases-and-servers-using-powershell/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Minor update to last backup report (1.3.1)</title>
		<link>http://www.flobee.net/minor-update-to-last-backup-report-1-3-1/</link>
		<comments>http://www.flobee.net/minor-update-to-last-backup-report-1-3-1/#comments</comments>
		<pubDate>Thu, 18 Dec 2008 14:46:54 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=402</guid>
		<description><![CDATA[Version 1.3.1 fixes a small issue where the subject would still say everything is okay even though the threshold for the last backup had been exceeded.&#160; (I didn&#8217;t set the scope of the variable correctly after moving part of the code into a function.)&#160; Download.]]></description>
			<content:encoded><![CDATA[<p>Version 1.3.1 fixes a small issue where the subject would still say everything is okay even though the threshold for the last backup had been exceeded.&nbsp; (I didn&#8217;t set the scope of the variable correctly after moving part of the code into a function.)&nbsp; <a href="http://www.flobee.net/download/LastBackupReport.zip">Download</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/minor-update-to-last-backup-report-1-3-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Powershell one-liner to remotely get SSL certificate expiration</title>
		<link>http://www.flobee.net/powershell-one-liner-to-remotely-get-ssl-certificate-expiration/</link>
		<comments>http://www.flobee.net/powershell-one-liner-to-remotely-get-ssl-certificate-expiration/#comments</comments>
		<pubDate>Wed, 10 Dec 2008 23:34:37 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=397</guid>
		<description><![CDATA[This is a quick and dirty way of retrieving the self-signed SSL certificate that Exchange 2007 servers use out of the box.&#160; Get-ExchangeCertificate only works locally on the box that has the certificate&#8230;not fun when you have a long list to find out so you can build a list for renewing them.&#160; This uses psexec [...]]]></description>
			<content:encoded><![CDATA[<p>This is a quick and dirty way of retrieving the self-signed SSL certificate that Exchange 2007 servers use out of the box.&nbsp; Get-ExchangeCertificate only works locally on the box that has the certificate&#8230;not fun when you have a long list to find out so you can build a list for renewing them.&nbsp; This uses psexec to connect to the remote server and retrieve any personal certificates and display the expiration date.&nbsp; Short and sweet.</p>
<pre class="brush:ps;gutter:false;wrap-lines:false;collapse:false;auto-links:false">
.\psexec \\server cmd /c &quot;echo . | powershell (gci -path cert:\localmachine\my).notafter&quot;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/powershell-one-liner-to-remotely-get-ssl-certificate-expiration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exchange 2007 backup script updated to v1.3</title>
		<link>http://www.flobee.net/exchange-2007-backup-script-updated-to-v1-3/</link>
		<comments>http://www.flobee.net/exchange-2007-backup-script-updated-to-v1-3/#comments</comments>
		<pubDate>Wed, 03 Dec 2008 23:25:19 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=395</guid>
		<description><![CDATA[This update fixes reporting for public folder databases.&#160; I failed to account for the fact that PF databases won&#8217;t be returned when using Get-MailboxDatabase.&#160; The structure of the script has changed a little bit to accommodate that, but the resulting change in the output is only that PF databases will now be included (and storage [...]]]></description>
			<content:encoded><![CDATA[<p>This update fixes reporting for public folder databases.&nbsp; I failed to account for the fact that PF databases won&#8217;t be returned when using Get-MailboxDatabase.&nbsp; The structure of the script has changed a little bit to accommodate that, but the resulting change in the output is only that PF databases will now be included (and storage groups that only had a PF database won&#8217;t report that no databases are present).&nbsp; <a href="http://www.flobee.net/download/LastBackupReport.zip">Download v1.3</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/exchange-2007-backup-script-updated-to-v1-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exchange 2007 backup script updated to v1.2</title>
		<link>http://www.flobee.net/exchange-2007-backup-script-updated-to-v1-2/</link>
		<comments>http://www.flobee.net/exchange-2007-backup-script-updated-to-v1-2/#comments</comments>
		<pubDate>Thu, 21 Aug 2008 17:51:29 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=393</guid>
		<description><![CDATA[I added&#160;notification of what the checkmark means, automatically load the Exchange snapin if not already, and append the subject of the email with a status summary.&#160; If there are no backups that need attention, all okay is added.&#160; If there are, it says that attention is needed.&#160; This provides a quick way to know if [...]]]></description>
			<content:encoded><![CDATA[<p>I added&nbsp;notification of what the checkmark means, automatically load the Exchange snapin if not already, and append the subject of the email with a status summary.&nbsp; If there are no backups that need attention, all okay is added.&nbsp; If there are, it says that attention is needed.&nbsp; This provides a quick way to know if anything needs attention.&nbsp; Download the script <a href="http://www.flobee.net/download/LastBackupReport.zip">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/exchange-2007-backup-script-updated-to-v1-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Another minor update to the Exchange 2007 backup script</title>
		<link>http://www.flobee.net/another-minor-update-to-the-exchange-2007-backup-script/</link>
		<comments>http://www.flobee.net/another-minor-update-to-the-exchange-2007-backup-script/#comments</comments>
		<pubDate>Mon, 11 Aug 2008 23:00:25 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=390</guid>
		<description><![CDATA[Version 1.11 correctly resets the checkmark status during each loop; when cleared before because no backup has taken place, it remained as a null value for later backups that were successful.&#160; Updated version is available here.]]></description>
			<content:encoded><![CDATA[<p>Version 1.11 correctly resets the checkmark status during each loop; when cleared before because no backup has taken place, it remained as a null value for later backups that were successful.&nbsp; Updated version is available <a href="http://www.flobee.net/download/LastBackupReport.zip">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/another-minor-update-to-the-exchange-2007-backup-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated Exchange 2007 database backup script</title>
		<link>http://www.flobee.net/updated-exchange-2007-database-backup-script/</link>
		<comments>http://www.flobee.net/updated-exchange-2007-database-backup-script/#comments</comments>
		<pubDate>Sat, 09 Aug 2008 01:57:36 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=387</guid>
		<description><![CDATA[I have made some minor updates to my PowerShell script that reports (and emails) the last backup of each database in your organization.&#160; I was referencing the wrong variable when looping through the databases in a storage group, so each storage group in the report listed all the databases on the server.&#160; I also change [...]]]></description>
			<content:encoded><![CDATA[<p>I have made some minor updates to my PowerShell script that reports (and emails) the last backup of each database in your organization.&nbsp; I was referencing the wrong variable when looping through the databases in a storage group, so each storage group in the report listed all the databases on the server.&nbsp; I also change the formatting a little so the text in different table cells in the same row actually look to be in the same row (the checkmark was offsetting that cell a bit).&nbsp; You can download v1.1 <a href="http://www.flobee.net/download/LastBackupReport.zip">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/updated-exchange-2007-database-backup-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PowerShell script to report last successful full backup of Exchange 2007</title>
		<link>http://www.flobee.net/powershell-script-to-report-last-successful-full-backup-of-exchange-2007/</link>
		<comments>http://www.flobee.net/powershell-script-to-report-last-successful-full-backup-of-exchange-2007/#comments</comments>
		<pubDate>Tue, 22 Jul 2008 14:28:27 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=297</guid>
		<description><![CDATA[Edit: The inline code in this post is not the latest version of the script. Get the latest version from the downloads page. This script is a port of my original backup report that was written in VBScript.&#160; That script reports on both 2003 and 2007 servers, but lacked some of the features that I [...]]]></description>
			<content:encoded><![CDATA[<p><font color="red">Edit:  The inline code in this post is not the latest version of the script.  Get the latest version from the <a href="/downloads">downloads</a> page.</font></p>
<p>This script is a port of my <a href="http://www.flobee.net/updated-last-backup-report-script/">original backup report</a> that was written in VBScript.&nbsp; That script reports on both 2003 and 2007 servers, but lacked some of the features that I wanted to put in.&nbsp; PowerShell natively supports date-awareness, which makes it much easier to add the number one feature I wanted to add: highlighting servers that haven&#8217;t had backups since a specified period of time.</p>
<p>Because I am using the native Exchange cmdlets instead of WMI or CDOEXM, this only reports on Exchange 2007 servers.&nbsp; I figure accommodating both is more work than it is worth, so I just modified my VBScript version to not include any server in the Exchange 12 admin group and I have both run every day until my migration to 2007 is complete.</p>
<p>The script reports the last successful full backup of any Exchange 2007 server with the mailbox role installed.&nbsp; It checks for the presence of storage groups and databases within them.&nbsp; It notes if a backup is currently in progress, as well as if a backup has never completed.&nbsp; If a backup has not completed in the last 72 hours (modifiable), it is highlighted in red so it is easy to spot.&nbsp; If a backup is less than the defined number of hours old, I use the Marlett font to display a green checkmark.&nbsp; This allows for a checkmark without having to reference an external image or embed one.&nbsp; Lastly, the report is emailed.&nbsp; The script is shown below, but you can also just <a href="http://www.flobee.net/download/LastBackupReport.zip">download</a> it.</p>
<pre class="brush:ps;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
#Last Backup Report for Exchange 2007 servers
#Version 1.0 - 7/9/08
#--------------------------------------------

#Begin customization-------------------------
$SmtpServer = "server.domain.com" #Enter FQDN of SMTP server
$SmtpFrom = "Exchange Backups &lt;exchangebackupreport@domain.com&gt;" #Enter sender email address
$SmtpTo = "user1@domain.com","user2@domain.com" #Enter one or more recipient addresses in an array
$SmtpSubject = "Exchange 2007 Last Backup Report" #Enter subject of message
$iNomHours = "72" #Enter number of hours since last backup that requires attention
#End customization---------------------------

$date = Get-Date
$sSpace = "&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"
$sOutput = "&lt;table&gt;"

#Checkmark to indicate last backup within nominal time
$sCheckMark = "&lt;span style=""font-family: Marlett; color: green; font-size: 14pt; font-weight: bold""&gt;a&lt;/span&gt;"

#Retrieve Exchange servers with mailbox role
$ExServer = Get-ExchangeServer | where {$_.IsMailboxServer -eq $True} | Sort-Object Name
Foreach ($server in $ExServer)
	{
	$sOutput += "&lt;tr&gt;&lt;td&gt;&lt;font size=2&gt;&lt;u&gt;&lt;b&gt;$server&lt;/b&gt;&lt;/u&gt;&lt;/font&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;"
	#Retrieve storage groups for a given server
	$StorageGroup = $server | Get-StorageGroup | Sort-Object Name
	#Check for absence of any storage groups
	If (($StorageGroup | Measure-Object Name).Count -eq $null)
		{
		$sOutput += "&lt;tr&gt;&lt;td&gt;&lt;font size=2&gt;" + $sSpace + "No storage groups present.&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;"
		}
	Else
		{
		Foreach ($sg in $StorageGroup)
			{
			$sOutput += "&lt;tr&gt;&lt;td&gt;&lt;font size=2&gt;" + $sSpace + $sg.Name + "&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;"
			#Retrieve mailbox databases for a given storage group
			$MailboxDatabase = $StorageGroup | Get-MailboxDatabase -Status | Sort-Object Name
			#Check for absence of any databases in storage group
			If (($MailboxDatabase | Measure-Object Name).Count -eq $null)
				{
				$sOutput += "&lt;tr&gt;&lt;td&gt;&lt;font size=2&gt;" + $sSpace + $sSpace + "No mailbox stores present.&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;"				}
			Else
				{
				Foreach ($db in $MailboxDatabase)
					{
					$sBackupRunning = ""
					#Note if backup is currently running
					If ($db.BackupInProgress -eq $true)
						{$sBackupRunning = "&lt;font size=2 color=blue&gt;(Backup In Progress)&lt;/font&gt;"}
					#Determine if backup has ever completed
					If ($db.LastFullBackup -ne $null)
						{
						$sBackupDay = $db.LastFullBackup.get_DayofWeek()
						$sBackupDateTime = $db.LastFullBackup.ToString("g")
						#Flag if last completed backup started longer than defined variable
						If (($date - $db.LastFullBackup).TotalHours -gt $iNomHours)
							{
							$sLastBackup = "&lt;font size=2&gt;Last Backup Started At: &lt;font color=red&gt;" + $sBackupDay + ", " + $sBackupDateTime + "&lt;/font&gt;&lt;/font&gt;"
							}
						Else
							{
							$sLastBackup = "&lt;font size=2&gt;Last Backup Started At: " + $sBackupDay + ", " + $sBackupDateTime + " &lt;/font&gt;" + $sCheckmark
							}
						}
					Else
						{
						$sLastBackup = "&lt;font size=2&gt;No full backup has completed yet.&lt;/font&gt;"
						}
					$sOutput += "&lt;tr&gt;&lt;td&gt;&lt;font size=2&gt;" + $sSpace + $sSpace + $db.Name + " &lt;/font&gt;&lt;/td&gt;&lt;td&gt;" + $sLastBackup + $sBackupRunning + "&lt;/td&gt;&lt;/tr&gt;"
					}
				}
			}
		}
	}
$sOutput += "&lt;/table&gt;"

#Email results
$SmtpClient = New-Object System.Net.Mail.SmtpClient
$MailMessage = New-Object System.Net.Mail.MailMessage
$SmtpClient.Host = $SmtpServer
$MailMessage.From = $SmtpFrom
Foreach ($address in $smtpTo)
	{$MailMessage.To.Add($address)}
$MailMessage.Subject = $SmtpSubject
$MailMessage.IsBodyHTML = $true
$MailMessage.Body = $sOutput
$SmtpClient.Send($MailMessage)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/powershell-script-to-report-last-successful-full-backup-of-exchange-2007/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Cannot enter a product key for a 64-bit Exchange 2007 server from the 32-bit management tools</title>
		<link>http://www.flobee.net/cannot-enter-a-product-key-for-a-64-bit-exchange-2007-server-from-the-32-bit-management-tools/</link>
		<comments>http://www.flobee.net/cannot-enter-a-product-key-for-a-64-bit-exchange-2007-server-from-the-32-bit-management-tools/#comments</comments>
		<pubDate>Wed, 04 Jun 2008 17:42:14 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2007]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=291</guid>
		<description><![CDATA[The 32-bit version of Exchange 2007 cano be used in a non-production environment only.&#160; As such, there is no need to license the product by entering a product key.&#160; Doing so will only result in the error &#34;Invalid product key&#34;. A side effect of this limitation is that you can also not enter a product [...]]]></description>
			<content:encoded><![CDATA[<p>The 32-bit version of Exchange 2007 cano be used in a non-production environment only.&nbsp; As such, there is no need to license the product by entering a product key.&nbsp; Doing so will only result in the error &quot;Invalid product key&quot;.</p>
<p>A side effect of this limitation is that you can also not enter a product key for a valid 64-bit server from a 32-bit installation such as the management tools on a 32-bit OS.&nbsp; I have a PowerShell script that configures a server, depending on the roles that are installed.&nbsp; However, the first thing it does for all servers is apply the product key.&nbsp; This fails because I am running my configuration script from a 32-bit management installation.</p>
<p>I consider this a bug.&nbsp; The only solution is to run the Set-ExchangeServer command with the -ProductKey parameter on a 64-bit installation of the management tools.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/cannot-enter-a-product-key-for-a-64-bit-exchange-2007-server-from-the-32-bit-management-tools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Convert a mailbox GUID to the user and display name</title>
		<link>http://www.flobee.net/convert-a-mailbox-guid-to-the-user-and-display-name/</link>
		<comments>http://www.flobee.net/convert-a-mailbox-guid-to-the-user-and-display-name/#comments</comments>
		<pubDate>Tue, 26 Feb 2008 20:39:21 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=277</guid>
		<description><![CDATA[If certain MAPI limits are reached when working with sessions, items, attachments, etc., Exchange will deny further access to that user to that object type.&#160; When this happens event ID 9646 is logged in the Application log.&#160; The description of the event contains a mailbox GUID that is causing the issue, but the GUID alone [...]]]></description>
			<content:encoded><![CDATA[<p>If certain MAPI limits are reached when working with sessions, items, attachments, etc., Exchange will deny further access to that user to that object type.&nbsp; When this happens event ID 9646 is logged in the Application log.&nbsp; The description of the event contains a mailbox GUID that is causing the issue, but the GUID alone does nothing to indicate what user/mailbox is affected.</p>
<p>Microsoft KB <a target="_blank" href="http://support.microsoft.com/kb/899663">899663</a>&nbsp;instructs how to manually transpose GUID into a format that can be used in an LDAP filter so that you can search for a match.&nbsp; Why do all this by hand when a script can do it for you?&nbsp; I took an existing script I had that already does the transposition and added an AD search to return the matching dn.&nbsp; The dn is passed to a name translation function that converts the dn to the NT4 format (domain\username) and displays the match with username and display name.</p>
<p>In addition to the VBScript file, I have also included a compiled version that uses SAPIEN Script Host as the engine.&nbsp; This is a self-contained, runs-in-memory-only, no-DOS-box-comes-up engine from PrimalScript.&nbsp; Running the compiled version is nice since you don&#8217;t have to ensure that CScript is the default host and no DOS box appears while the script is running.</p>
<p>The zip file with both versions is available <a href="http://www.flobee.net/download/MbxGUIDtoMbxName.zip">here </a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/convert-a-mailbox-guid-to-the-user-and-display-name/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Updated: Copy DLs from one user to another</title>
		<link>http://www.flobee.net/updated-copy-dls-from-one-user-to-another/</link>
		<comments>http://www.flobee.net/updated-copy-dls-from-one-user-to-another/#comments</comments>
		<pubDate>Sun, 22 Jul 2007 23:01:09 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=202</guid>
		<description><![CDATA[The first version of the script really was quick and dirty, requiring you to manually put the source and target users&#8217; DNs in the script.&#160; Since a coworker has been using the script, I thought it appropriate to update it to prompt for the usernames.&#160; In addition, I added a new feature I recently read [...]]]></description>
			<content:encoded><![CDATA[<p>The first version of the script really was quick and dirty, requiring you to manually put the source and target users&#8217; DNs in the script.&nbsp; Since a coworker has been using the script, I thought it appropriate to update it to prompt for the usernames.&nbsp; In addition, I added a new feature I recently read about, which is to output the results in real-time to a GUI.&nbsp; This is done by creating an object for IE and writing the output similar to wscript.echo, but with the Write method of the object.</p>
<p>Like the original script, since we use automated DLs, too, I look for an indication that a given DL is a SmartDL and skip it.&nbsp; And I now use PrimalScript to work with my scripts, so I use its packager to make an exectuable.&nbsp; This makes it easier and nicer for non-IT end-users who will be running scripts like these.</p>
<p>Download it <a href="http://www.flobee.net/download/CopyDLMembership.zip">here</a>, or copy/paste below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
'Version 2.0 - July 23, 2007
'Copy distribution group membership from one user to another,
'excluding automated DLs (SmartDL).
'Get source user
While Not bolExit = True
	strOldSamUser = InputBox(&quot;Enter the sAMAccountName of the person to copy DLs FROM.&quot; _
		, &quot;Enter username&quot;)
	If strOldSamUser = &quot;&quot; Then
		WScript.Quit
	End If

	'Find the Global Catalog server
	Set objCont = GetObject(&quot;GC:&quot;)
	For Each objGC In objCont
		strADsPath = objGC.ADsPath
	Next

	Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
	Set objRecordset = CreateObject(&quot;ADODB.Recordset&quot;)
	objConnection.Provider = &quot;ADsDSOObject&quot;

	objConnection.Open &quot;ADs Provider&quot;
	strQuery = &quot;&lt;&quot; &amp; strADsPath &amp; &quot;&gt;;(&amp;(objectcategory=user)(sAMAccountName=&quot; &amp; strOldSamUser &amp; _
		&quot;));displayName,distinguishedName;subtree&quot;
	Set objRecordset = objConnection.Execute(strQuery)

	If Trim(objRecordset.Fields(&quot;distinguishedName&quot;)) = &quot;&quot; Then
		strNoUser = MsgBox(&quot;Warning: User cannot be found.&nbsp; Verify sAMAccountName.&quot;, vbCritical, &quot;User not found!&quot;)
		bolExit = False
	Else
		intCorrectUser = MsgBox(&quot;Is this the correct user?&quot; &amp; VbCrLf &amp; VbCrLf &amp; &quot;Display Name: &quot; &amp; _
		objRecordset.Fields(&quot;displayName&quot;) &amp; VbCrLf &amp; &quot;DN: &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;), _
			vbYesNo, &quot;Old user?&quot;)
		If intCorrectUser &lt;&gt; 6 Then
			bolExit = False
		Else
			strSrcDN = objRecordset.Fields(&quot;distinguishedName&quot;)
			bolExit = True
		End If
	End If
 Wend
'Open IE to display progress and results
Set objIE = CreateObject(&quot;InternetExplorer.Application&quot;)
objIE.AddressBar = False
objIE.Menubar = False
objIE.Toolbar = False
objIE.Resizable = True
objIE.Left = 10
objIE.Height = 450
objIE.Width = 800
objIE.Visible = True
objIE.Navigate(&quot;about:blank&quot;)
While objIE.Busy
	WScript.Sleep 100
Wend
Set objDoc = objIE.Document
objDoc.Open
objDoc.Write(&quot;&lt;TITLE&gt;Copy DL Membership&lt;/TITLE&gt;&quot;)
objDoc.Write(&quot;&lt;BODY BGCOLOR=#C0C0C0&gt;&quot;)
objDoc.Write(&quot;&lt;P&gt;&lt;b&gt;Source:&lt;/b&gt; &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;) &amp; &quot;&lt;br&gt;&quot;)
'Get target user
bolExit = False
While Not bolExit = True
	strNewSamUser = InputBox(&quot;Enter the sAMAccountName of the person to copy DLs TO.&quot; _
		, &quot;Enter username&quot;)
	If strNewSamUser = &quot;&quot; Then
		objIE.Quit
		WScript.Quit
	End If
	strQuery = &quot;&lt;&quot; &amp; strADsPath &amp; &quot;&gt;;(&amp;(objectcategory=user)(sAMAccountName=&quot; &amp; strNewSamUser &amp; _
		&quot;));displayName,distinguishedName;subtree&quot;
	Set objRecordset = objConnection.Execute(strQuery)
	If Trim(objRecordset.Fields(&quot;distinguishedName&quot;)) = &quot;&quot; Then
		strNoUser = MsgBox(&quot;Warning: User cannot be found.&nbsp; Verify sAMAccountName.&quot;, vbCritical, &quot;User not found!&quot;)
		bolExit = False
	Else
		intCorrectUser = MsgBox(&quot;Is this the correct user?&quot; &amp; VbCrLf &amp; VbCrLf &amp; &quot;Display Name: &quot; &amp; _
			objRecordset.Fields(&quot;displayName&quot;) &amp; VbCrLf &amp; &quot;DN: &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;), _
			vbYesNo, &quot;Old user?&quot;)
		If intCorrectUser &lt;&gt; 6 Then
			bolExit = False
		Else
			Set objTargetUser = GetObject(&quot;LDAP://&quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;))
			bolExit = True
		End If
	End If
 Wend
 'Write target user to IE window
 objDoc.Write(&quot;&lt;b&gt;Target:&lt;/b&gt; &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;) &amp; &quot;&lt;/P&gt;&quot;)
 'Copy DLs
 strDomFQDN = Mid(strSrcDN, InStr(LCase(strSrcDN), &quot;,dc=&quot;) + 4)
 strGCFQDN = Replace(LCase(strDomFQDN), &quot;,dc=&quot;, &quot;.&quot;)
 Set objOldUser = GetObject(&quot;GC://&quot; &amp; strGCFQDN &amp; &quot;/&quot; &amp; strSrcDN)
 For Each strGroup in objOldUser.MemberOf
	On Error Resume Next
	Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroup)
	If Not Trim(objGroup.mailNickname) = &quot;&quot; Then
		If Not Instr(objGroup.info, &quot;SmartDL&quot;) &gt; 0 Then
			objGroup.Add(objTargetUser.ADsPath)
			If Err.Number = 0 Then
				objDoc.Write(objGroup.DisplayName &amp; &quot;: Update successful.&lt;br&gt;&quot;)
			Else
				objDoc.Write(objGroup.DisplayName &amp; &quot;: Update UNSUCCESSFUL.&lt;br&gt;&quot;)
			End If
		Else
			objDoc.Write(objGroup.DisplayName &amp; &quot;: Skipped (SmartDL).&lt;br&gt;&quot;)
		End If
	End If
	On Error Goto 0
 Next

 Set objOldUser = Nothing
 Set objTargetUser = Nothing
 Set objIE = Nothing
 Set objRecordset = Nothing
 Set objConnection = Nothing
 </pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/updated-copy-dls-from-one-user-to-another/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick and dirty script to copy DLs from one user to another</title>
		<link>http://www.flobee.net/quick-and-dirty-script-to-copy-dls-from-one-user-to-another/</link>
		<comments>http://www.flobee.net/quick-and-dirty-script-to-copy-dls-from-one-user-to-another/#comments</comments>
		<pubDate>Sun, 04 Feb 2007 23:07:35 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=187</guid>
		<description><![CDATA[Edit: The inline code in this post is not the latest version of the script. Get the latest version from the downloads page. It is not uncommon at my company to have to move a user from one domain to another for technical, logistical, or political reasons.  For another set of reasons, moving the user [...]]]></description>
			<content:encoded><![CDATA[<p><font color="red">Edit:  The inline code in this post is not the latest version of the script.  Get the latest version from the <a href="/downloads">downloads</a> page.</font></p>
<p>It is not uncommon at my company to have to move a user from one domain to another for technical, logistical, or political reasons.  For another set of reasons, moving the user account to the other domain is not done, instead manually creating a new one and associating the mailbox with the new account.</p>
<p>DL membership does not automatically get updated when this is done, so I have been doing it manually.  It has been on my to-do list for awhile to write a script to copy the DL membership from the old account to the new one.  So I threw this together this morning.  It lacks some of the nice extras my other scripts have (finding the user by logon name, email results, true logging) but it does work.</p>
<p>You have to edit the script to give it the variables for the old and new users&#8217; dn.  It will skip security groups (any group without an alias) and also groups whose Notes (info) attribute contains the word SmartDL.  We use Imanami&#8217;s SmartDL for automated DL membership when applicable.  Those will be automatically updated the next time each of their jobs run.</p>
<p>Download it <a title="Copy DLs from one user to another" href="http://www.flobee.net/download/CopyDLMembership.zip">here</a>, or copy/paste below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit
Dim strOldUser, strNewUser, objOldUser, objNewUser, strGroup, objGroup
strOldUser = &quot;&quot; 'dn of user to copy from'
strNewUser = &quot;&quot; 'dn of user to copy to'
Set objOldUser = GetObject(&quot;LDAP://&quot; &amp; strOldUser)
Set objNewUser = GetObject(&quot;LDAP://&quot; &amp; strNewUser)
wscript.echo &quot;Source user: &quot; &amp; objOldUser.DisplayName
wscript.echo &quot;Target user: &quot; &amp; objNewUser.DisplayName
For Each strGroup in objOldUser.MemberOf
	On Error Resume Next
	Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroup)
    If Not Trim(objGroup.mailNickname) = &quot;&quot; Then
        If Not Instr(objGroup.info, &quot;SmartDL&quot;) &gt; 0 Then
			objGroup.Add(objNewUser.ADsPath)
			If Err.Num = 0 Then
				wscript.echo objGroup.DisplayName &amp; &quot;: Update successful.&quot;
			Else
				wscript.echo objGroup.DisplayName &amp; &quot;: Update UNSUCCESSFUL.&quot;
			End If
        Else
			wscript.echo objGroup.DisplayName &amp; &quot;: Skipped (SmartDL).&quot;
        End If
	End If
	On Error Goto 0
Next
Set objOldUser = Nothing
Set objNewUser = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/quick-and-dirty-script-to-copy-dls-from-one-user-to-another/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated Exchange ActiveSync disable script</title>
		<link>http://www.flobee.net/updated-exchange-activesync-disable-script/</link>
		<comments>http://www.flobee.net/updated-exchange-activesync-disable-script/#comments</comments>
		<pubDate>Sun, 03 Dec 2006 19:48:33 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=172</guid>
		<description><![CDATA[I previously posted (here) a script to disable Exchange ActiveSync for unauthorized users.  The script is based on using the dn of one or more groups to determine authorized users (i.e., if you are in the group, you are allowed).  This method recently failed me (twice) because some of the groups were renamed by another admin [...]]]></description>
			<content:encoded><![CDATA[<p>I previously posted (<a title="Disable Exchange ActiveSync post" href="http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/">here</a>) a script to disable Exchange ActiveSync for unauthorized users.  The script is based on using the dn of one or more groups to determine authorized users (i.e., if you are in the group, you are allowed).  This method recently failed me (twice) because some of the groups were renamed by another admin to make them more readable.  Not only were the display names changed, but the objects were renamed as well, so the cn and dn were changed.  This means my script couldn&#8217;t find the groups I had hard-coded, which isn&#8217;t good scripting practice anyway.</p>
<p>I have updated the script, which can be downloaded <a title="Exchange ActiveSync disable script" href="http://www.flobee.net/download/EASDisable.zip">here</a>, to search for the groups based on their objectGUID, which never changes no matter what you do to the object (rename, move, etc.).  Instead of hard-coding the dn of the object, I hard-code the objectGUID.  Then I bind directly to the object based on the GUID to retrieve its dn, which is used in the search filter to find the users I want to modify.</p>
<p>Note that ADO allows you to bind to an object given just its GUID, without having to specify other connection parameters.  You can also use the hexadecimal or binary format of the GUID in the connection string; AD will figure out which format you are using. I used the binary format in my script so that I could just copy and paste the value from <a title="SystemTools' Hyena" href="http://www.systemtools.com/hyena/index.html" target="_blank">Hyena</a> into my script without having to convert to a hex value.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/updated-exchange-activesync-disable-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Change users&#8217; default calendar permission (and email the users and admin)</title>
		<link>http://www.flobee.net/change-users-default-calendar-permission-and-email-the-users-and-admin/</link>
		<comments>http://www.flobee.net/change-users-default-calendar-permission-and-email-the-users-and-admin/#comments</comments>
		<pubDate>Wed, 21 Jun 2006 23:49:31 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=152</guid>
		<description><![CDATA[Our IT Leadership team decided that it will promote more effective collaboration if the Default calendar permission for all IT employees is set to Reviewer (instead of the Default of None). As with many of my scripts, I started with the work Glen Scales has already done. Go there for information on the prerequisite for [...]]]></description>
			<content:encoded><![CDATA[<p>Our IT Leadership team decided that it will promote more effective collaboration if the Default calendar permission for all IT employees is set to Reviewer (instead of the Default of None). As with many of my scripts, I started with the work <a href="http://gsexdev.blogspot.com/2005/05/changing-default-permissions-on.html" target="_blank" alt="Glen Scales' original script to set the default permission to Reviewer.">Glen Scales</a> has already done. Go there for information on the prerequisite for using this script to make the change (the need for ACL.dll).</p>
<p>Glen&#8217;s script uses an input argument of a server name. My distributed environment doesn&#8217;t allow for such a broad application, so I use the membership of a DL that contains the IT personnel. This DL actually contains other DLs, so I separately use another <a href="http://www.rlmueller.net/List%20Members%20of%20a%20Group.htm" target="_blank" alt="VBSCript to enumerate all members of a group, event nested members.">script</a> by Richard Mueller that enumerates nested groups and puts the results into a domain local group that is used for other purposes.</p>
<p>After getting the group members, I loop through each member for only those with a mailbox that isn&#8217;t hidden. For those who haven&#8217;t been touched by the script before, a function is called that uses CDO to log into the mailbox, enumerate the calendar permissions looking for the Default entry, and change it to Reviewer. The user&#8217;s local FreeBusy Data folder is also updated to set the Default to Editor since that permission goes hand in hand when setting permissions on the calendar.</p>
<p>To keep tabs on who the script has touched, a notation is added to extensionAttribute4 (customizable). Those with that notation are skipped in the future. Then an email is sent to the user informing them of the change. I chose to do this because Exchange users may be used to the fact the the Default permission is None; this way they are aware that their calendar is open for viewing. It also helps with new employees who are not aware of this &quot;policy&quot; and can mark items as Private as necessary.</p>
<p>Finally, an email is sent to the admin with the results of the job run. It includes the display name, mailbox server, and whether the permission was set or the object skipped (to catch entries that slip through my filter). I have scheduled this to run weekly, and I will get an email each time so I know that the script is successfully running and which users are being modified.</p>
<p>All of the variables that require customization are near the top. These are for mail configuration, search filter, and the notation parameters for touched mailboxes.</p>
<p>Download it <a href="http://www.flobee.net/download/DefaultCalPerm.zip">here</a> or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit

Public Const CdoDefaultFolderCalendar = 0
Dim strSMTPServer, strMailFrom, strUserMailSubject, strUserMailBody
Dim strAdminMailRecip, strAdminMailSubject, strAdminMailBody
Dim strDefaultNamingContext, strQueryFilter, strAttName, strAttNote
Dim conn, com, iAdRootDSE, strNamingContext
Dim Rs, objGroup, strMember, objUser, strMsExchHomeServerName
Dim objSession, CdoInfoStore, CdoFolderRoot, ACLObj, CdoCalendar, FolderACEs, fldACE
Dim objRoot, objFreeBusyFolder

'Configuration parameters:
''''''''''''''''''''''''''''''''''''''''''''''
'Email notification configuration
strSMTPServer = &quot;server.company.com&quot; 'FQDN of SMTP server
strMailFrom = &quot;&quot;&quot;Display Name&quot;&quot; &lt;a href="mailto:SMTPAddress@company.com"&gt;SMTPAddress@company.com&lt;/a&gt;&quot; ' Display name and address of sender
strUserMailSubject = &quot;The default permission on your calendar has been updated&quot; 'Subject of message sent to users
strUserMailBody = &quot;In order to promote effective collaboration among the Company &quot; &amp; _
    &quot;employees, the Default permission on your calendar has been updated to Reviewer.  This allows anyone within the organization &quot; &amp; _
    &quot;to see the details of items within your calendar.  For items that are of a sensitive, confidential, or personal nature, &quot; &amp; _
    &quot;you can mark them as Private.  This will restrict the details of the item so that others cannot see the subject or body.  &quot; &amp; _
    &quot;If you have any questions about this change, please reply to this message or contact the Help Desk at XXXX.&lt;br&gt;&lt;br&gt;&quot; &amp; _
    &quot;Company Messaging and Collaboration Team (GAL Display Name)&lt;br&gt;Department&lt;br&gt;Company&quot; 'Body of message sent to users
strAdminMailRecip = &quot;&lt;a href="mailto:SMTPAddress@company.com"&gt;SMTPAddress@company.com&lt;/a&gt;&quot; 'Address of admin to receive status report
strAdminMailSubject = &quot;Default calendar permission change report&quot; 'Subject of message sent to admin
'Domain to search for object
strDefaultNamingContext = &quot;dc=company,dc=com&quot; 'AD FQDN of base scope to search
'Search filter for group with users
strQueryFilter = &quot;(&amp;(objectcategory=group)(displayName=something))&quot; 'LDAP filter to locate group with members to modify

'Attribute and notation for updated objects
strAttName = &quot;extensionAttribute4&quot; 'defined for a single-valued string attribute
strAttNote = &quot;DefaultCalSet&quot; 'Notation used to skip processed users on future script runs
''''''''''''''''''''''''''''''''''''''''''''''
strAdminMailBody = &quot;&quot;
Set conn = createobject(&quot;ADODB.Connection&quot;)
Set com = createobject(&quot;ADODB.Command&quot;)
Set iAdRootDSE = GetObject(&quot;&lt;a href="ldap://RootDSE"&gt;LDAP://RootDSE&lt;/a&gt;&quot;)
strNamingContext = iAdRootDSE.Get(&quot;configurationNamingContext&quot;)
Conn.Provider = &quot;ADsDSOObject&quot;
Conn.Open &quot;ADs Provider&quot;
Com.ActiveConnection = Conn
com.Properties(&quot;Page Size&quot;) = 1000
Com.CommandText = &quot;&lt;GC://&quot; &amp; strDefaultNamingContext &amp; &quot;&gt;;&quot; &amp; StrQueryFilter &amp; &quot;;distinguishedname;subtree&quot;
Set Rs = Com.Execute
While Not Rs.EOF
	Set objGroup = GetObject(&quot;LDAP://&quot; &amp; Rs.fields(&quot;distinguishedname&quot;))
	For each strMember in objGroup.Member
		Set objUser = GetObject(&quot;LDAP://&quot; &amp; strMember)
		If InStr(objUser.Get(strAttName), strAttNote) &lt; 1 Then
			strAdminMailBody = strAdminMailBody &amp; objUser.displayName &amp; &quot;&lt;br&gt;&quot;
			If objUser.Class = &quot;user&quot; And Not IsNull(objUser.msExchHomeServerName) And Not UCase(objUser.msExchHidefromAddressLists) = &quot;TRUE&quot; Then
				strMsExchHomeServerName = objUser.msExchHomeServerName
				strMsExchHomeServerName = right(strMsExchHomeServerName,len(strMsExchHomeServerName)-instrrev(strMsExchHomeServerName,&quot;/cn=&quot;)-3)
				strAdminMailBody = strAdminMailBody &amp; &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot; &amp; strMsExchHomeServerName &amp; &quot;&lt;br&gt;&quot;
				Call dofreebusy(strMsExchHomeServerName, objUser.mailNickname)
				strAdminMailbody = strAdminMailBody &amp; &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Permission set on: &quot; &amp; objUser.mailNickname &amp; &quot;&lt;br&gt;&quot;
				WriteTag
				SendEmail objUser.mail, strUserMailSubject, strUserMailBody
			Else
				strAdminMailBody = strAdminMailBody &amp; &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Skipping object&lt;br&gt;&quot;
			End If
		End If
	Next
	Rs.MoveNext
Wend
Rs.Close

'Send admin email report
If Not strAdminMailBody = &quot;&quot; Then
	strAdminMailBody = strAdminMailBody &amp; &quot;&lt;br&gt;Done.&lt;br&gt;&quot;
Else
	strAdminMailBody = &quot;No mailboxes needed updating.&quot;
End If
SendEmail strAdminMailRecip, strAdminMailSubject, strAdminMailBody

Set conn = Nothing
Set com = Nothing

Function dofreebusy(serverName, mailboxName)
	'Set Default permission to Reviewer
	Set objSession = CreateObject(&quot;MAPI.Session&quot;)
	objSession.Logon &quot;&quot;,&quot;&quot;,false,true,0,true,servername &amp; vbLF &amp; mailboxname
	Set CdoInfoStore = objSession.GetInfoStore
	Set CdoFolderRoot = CdoInfoStore.RootFolder
	Set ACLObj = CreateObject(&quot;MSExchange.aclobject&quot;)
	Set CdoCalendar = objSession.GetDefaultFolder(CdoDefaultFolderCalendar)
	ACLObj.CdoItem = CdoCalendar
	Set FolderACEs = ACLObj.ACEs
	For each fldACE in FolderACEs
		If fldACE.ID = &quot;ID_ACL_DEFAULT&quot; Then
			fldACE.Rights = 1025
			ACLObj.Update
		End If
	Next

	'Set local FreeBusy folder permission to Editor
	Set objRoot = objSession.GetFolder(&quot;&quot;)
	Set objFreeBusyFolder = objRoot.Folders.Item(&quot;FreeBusy Data&quot;)
	ACLObj.CdoItem = objFreeBusyFolder
	Set FolderACEs = ACLObj.ACEs
	For each fldACE in FolderACEs
		If fldACE.ID = &quot;ID_ACL_DEFAULT&quot; Then
		fldACE.Rights = 1123
		ACLObj.Update
		End If
	Next
End function

'Write notation tag into attribute
Sub WriteTag()
	If IsNull(objUser.Get(strAttName)) or IsEmpty(objUser.Get(strAttName)) Then
		objUser.Put strAttName, strAttNote &amp; &quot;;&quot;
	Else
		Dim strExistAttName
		strExistAttName = objUser.Get(strAttName)
		objUser.Put strAttName, strExistAttName &amp; strAttNote &amp; &quot;;&quot;
	End If
	objUser.SetInfo
End Sub

'Send change notification and report
Sub SendEmail (strRecipAddress, strMailSubject, strMailBody)
	Dim objMail
	Set objMail = CreateObject(&quot;CDO.Message&quot;)
	objMail.Configuration.Fields.Item (&quot;<a href="http://schemas.microsoft.com/cdo/configuration/sendusing">http://schemas.microsoft.com/cdo/configuration/sendusing</a>&quot;) = 2
	objMail.Configuration.Fields.Item (&quot;<a href="http://schemas.microsoft.com/cdo/configuration/smtpserver">http://schemas.microsoft.com/cdo/configuration/smtpserver</a>&quot;) = strSMTPServer
	objMail.Configuration.Fields.Item (&quot;<a href="http://schemas.microsoft.com/cdo/configuration/smtpserverport">http://schemas.microsoft.com/cdo/configuration/smtpserverport</a>&quot;) = 25
	objMail.Configuration.Fields.Update

 objMail.From = strMailFrom
	objMail.To = strRecipAddress
	objMail.Subject = strMailSubject
	objMail.HTMLBody = strMailBody
	objMail.Send
	Set objMail = Nothing
End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/change-users-default-calendar-permission-and-email-the-users-and-admin/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Script to disable Exchange ActiveSync for unauthorized users</title>
		<link>http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/</link>
		<comments>http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/#comments</comments>
		<pubDate>Thu, 01 Jun 2006 16:09:11 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=147</guid>
		<description><![CDATA[By default, users have all mobile services enabled (OMA, EAS including AUTD/DP).  This is a pain in my environment because only authorized users are allowed to use EAS (to ensure only approved devices procured through proper channels are used).  OMA, being similar to OWA, is allowed for everyone. I had written a batch file long ago [...]]]></description>
			<content:encoded><![CDATA[<p>By default, users have all mobile services enabled (OMA, EAS including AUTD/DP).  This is a pain in my environment because only authorized users are allowed to use EAS (to ensure only approved devices procured through proper channels are used).  OMA, being similar to OWA, is allowed for everyone.</p>
<p>I had written a batch file long ago to change the bitmask attribute for users whose mobile services are enabled for everything (0) and are not in the appropriate DL of authorized users to disable only EAS (5).  It was an inefficient script that required explicit permissions for each domain, called a command-line regex tool to format the ldifde export, and was prone to errors.</p>
<p>This updated script accomplishes the same thing, but more efficiently.  It processes all users at one time (inside a for loop) and uses implicit permissions.  It even emails the results of the number of users modified.  The script is customized for my environment, but you can tweak it as necessary.  </p>
<p>I have five user domains, but the DLs for authorization are in one domain.  I wanted it to be as dynamic as possible, but balancing that with all the extra code necessary to make every piece not rely on hard-coded information.  So you need to provide mail config information, the NetBIOS domain names you want to loop through, and the dn of the groups for each user domain.  The GC to search and the DC to make the change to will be determined automatically.</p>
<p>Download it <a href="http://www.flobee.net/download/EASDisable.zip">here</a> or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit

Dim mailFrom, mailTo, mailSubject, mailBody, mailServer
Dim objConnection, objCommand, objRecordSet, objUser, objGC, objDomain
Dim strGCPath, strNBDomain, strdomain, arrDomains, strCount
dim strSearchFilter, strEASDL, strDomainPath
strCount = ""

'Mail config
mailFrom = Wscript.ScriptName &#038; "@company.com"
mailTo = "user@company.com"
mailSubject = "EAS Disable Results"
mailBody = ""
mailServer = "server.company.com"

'AD connection parameters
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection

'Get FQDN of a GC
Set objGC = GetObject("GC://RootDSE")
strGCPath = objGC.Get("dnsHostName")

'Query each domain and call disable subroutine
arrDomains = array("domainA", "domainB", "domainC", "domainD", "domainE")
For each strDomain in arrDomains
	subOMADisable strdomain
Next

'Email results
Call subSendMail(mailFrom, mailTo, mailSubject, strCount, mailServer)

'Sub to change attribute value
Sub subOMADisable(strNBDomain)
	'Get base DN of domain
	Set objDomain = GetObject("LDAP://" &#038; strNBDomain &#038; "/RootDSE")
	strDomainPath = objDomain.Get("DefaultNamingContext")
	Set objDomain = Nothing

	Select Case strNBDomain
		Case "domainA"
			strEASDL = "dn of groupA"
		Case "domainB"
			strEASDL = "dn of groupB"
		Case "domainC"
			strEASDL = "dn of groupC"
		Case "domainD"
			strEASDL = "dn of groupD"
		Case "domainE"
			strEASDL = "dn of groupE"
	End Select
	strSearchFilter="(&#038;(objectcategory=user)(mailnickname=*)(homeMDB=*)(!memberof=" &#038; strEASDL &#038; ")(!msExchOMAAdminWirelessEnable=5))"
	objCommand.CommandText = "&lt;GC://" &#038; strGCPath &#038; "/" &#038; strDomainPath &#038; "&gt;;" &#038; strSearchFilter &#038; ";distinguishedname;subtree"
	Set objRecordSet = objCommand.Execute
	If objRecordSet.RecordCount &gt; 0 Then
		While Not objRecordSet.EOF
			Set objUser = GetObject("LDAP://" &#038; objRecordSet.Fields("distinguishedname"))
			'wscript.echo objUser.DisplayName
			objUser.Put "msExchOMAAdminWirelessEnable", "5"
			objUser.SetInfo
			objRecordSet.MoveNext
		Wend
	End If
	strCount = strCount &#038; vbTAB &#038; strNBDomain &#038; ": " &#038; objRecordSet.RecordCount &#038; vbCRLF
End Sub

'Sub to send email with results
Sub subSendMail (mFrom, mTo, mSubject, mBody, mServer)
	Dim objEmail
	Set objEmail = CreateObject("CDO.Message")
	objEmail.From = mFrom
	objEmail.To = mTo
	objEmail.Subject = mSubject
	objEmail.Textbody = "Objects modified in each domain:" &#038; vbCRLF &#038;vbCRLF &#038; mBody
	objEmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	objEmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = mServer
	objEmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
	objEmail.Configuration.Fields.Update
	objEmail.Send
End Sub

'Cleanup
Set objUser = Nothing
Set objCommand = Nothing
Set objGC = Nothing
Set objConnection = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Use Outlook rules to delete public folder conflict messages</title>
		<link>http://www.flobee.net/use-outlook-rules-to-delete-public-folder-conflict-messages/</link>
		<comments>http://www.flobee.net/use-outlook-rules-to-delete-public-folder-conflict-messages/#comments</comments>
		<pubDate>Mon, 27 Feb 2006 15:03:06 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=88</guid>
		<description><![CDATA[I am in a DL that has explicit ownership permissions on all public folders in my org (all 22,881 of them).  And since Exchange sends public folder conflict messages to the owner(s) of a folder, I get quite a few of them.  At one time I thought I had a rule to move or delete [...]]]></description>
			<content:encoded><![CDATA[<p>I am in a DL that has explicit ownership permissions on all public folders in my org (all 22,881 of them).  And since Exchange sends public folder conflict messages to the owner(s) of a folder, I get quite a few of them.  At one time I thought I had a rule to move or delete them, but I couldn&#8217;t get it to work when I tried to set it up again.  Because the message class is different (IPM.Conflict.Folder) you don&#8217;t get to see the same fields as a regular message.  Rules to delete them based on words in the sender&#8217;s address, etc., had no effect, partly because the sender is the name of the folder that has the conflict, so it is a dynamic value.</p>
<p>Using MFCMapi to look at the properties of a conflict message, there are several properties that you&#8217;d think you could use, but when setting up a rule to use properties of the conflict message form, none of he properties are available.  And if you manually type in property name it gives you an error.</p>
<p>In the end, I tried setting the rule again to fire on subject contains &quot;Conflict Message:&quot; and it worked.  Huh.  So who knows what I was doing wrong before?  You can also have the rule fire if the message form is &quot;Conflict Message.&quot;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/use-outlook-rules-to-delete-public-folder-conflict-messages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Send email notification for password expiration to &#8220;remote&#8221; users</title>
		<link>http://www.flobee.net/send-email-notification-for-password-expiration-to-remote-users/</link>
		<comments>http://www.flobee.net/send-email-notification-for-password-expiration-to-remote-users/#comments</comments>
		<pubDate>Mon, 19 Sep 2005 16:18:47 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=9</guid>
		<description><![CDATA[Over on Michael Smith&#8217;s blog, he has a script to notify users when their password is about to expire.  This is handy for Exchange users who never log in to the network because they are offsite or on an extranet (i.e., POP3/IMAP4/RPC-HTTP users).  It will crawl an OU and check the days until expiration and, [...]]]></description>
			<content:encoded><![CDATA[<p>Over on Michael Smith&#8217;s <a href="http://blogs.brnets.com/michael" target="_blank">blog</a>, he has a <a href="http://blogs.brnets.com/michael/archive/2005/09/13/1474.aspx" target="_blank">script</a> to notify users when their password is about to expire.  This is handy for Exchange users who never log in to the network because they are offsite or on an extranet (i.e., POP3/IMAP4/RPC-HTTP users).  It will crawl an OU and check the days until expiration and, if less than a variable you set, send an email via CDO.</p>
<p>However, at my office we don&#8217;t have these kinds of users in one OU.  They are spread across multiple domains, but are all in a local group that has permission to a web page that allows them to change their password.  So I updated the script to enumerate the members of this group.  To accommodate different password aging settings in each domain I moved the domain query for this setting into the For loop.  I also added logging so you will have a record of what was sent, not sent, and why.  This way you can schedule it or run it interactively without having to adjust the code.  Download it <a href="http://www.flobee.net/download/PwdExpEmail.zip">here</a>, or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit

' Per environment constants - you should change these
Const SMTP_SERVER  = "ServerName"
Const STRFROM   = "From SMTP Address"
Const DAYS_FOR_EMAIL  = 14 'Send notification when pwd will expire in this number of days
Const GROUPDN = "DN of group to enumerate"
Const LOGFILE = "Patch and filename of log file"

' System Constants - do not change
Const ONE_HUNDRED_NANOSECOND    = .000000100   ' .000000100 is equal to 10^-7
Const SECONDS_IN_DAY            = 86400
Const ADS_UF_DONT_EXPIRE_PASSWD = &amp;h10000
Const E_ADS_PROPERTY_NOT_FOUND  = &amp;h8000500D

' Change to "True" for extensive debugging output
Const bDebug   = False

Dim numDays, iResult
Dim strDomain
Dim objGroup, objMember, member
Dim objFSO, objFile, strOutput

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(LOGFILE, 8, True)

Set objGroup = GetObject("LDAP://" &amp; GROUPDN)
objFile.WriteLine "Executed at " &amp; Now() &amp; vbCRLF
objFile.WriteLine "Enumerating members of " &amp; objGroup.distinguishedName &amp; ":" &amp; vbCRLF
For each member in objGroup.member
	Set objMember = GetObject("LDAP://" &amp; member)
	objFile.WriteLine objMember.distinguishedname
	strDomain = Mid(objMember.distinguishedname, InStr(objMember.distinguishedname, "DC="))
	numdays = GetMaximumPasswordAge (strDomain)
	Call ProcessUser (numDays)
Next

objFile.WriteLine "Done. Finished at " &amp; Now() &amp; "."
objFile.Close

Function GetMaximumPasswordAge (ByVal strDomainDN)
	Dim objDomain, objMaxPwdAge
	Dim dblMaxPwdNano, dblMaxPwdSecs, dblMaxPwdDays

	Set objDomain = GetObject("LDAP://" &amp; strDomainDN)
	Set objMaxPWdAge = objDomain.maxPwdAge

	If objMaxPwdAge.LowPart = 0 And objMaxPwdAge.Highpart = 0 Then
		' Maximum password age is set to 0 in the domain
		' Therefore, passwords do not expire
		GetMaximumPasswordAge = 0
	Else
		dblMaxPwdNano = Abs (objMaxPwdAge.HighPart * 2^32 + objMaxPwdAge.LowPart)
		dblMaxPwdSecs = dblMaxPwdNano * ONE_HUNDRED_NANOSECOND
		dblMaxPwdDays = Int (dblMaxPwdSecs / SECONDS_IN_DAY)
		GetMaximumPasswordAge = dblMaxPwdDays
	End If
End Function

Function UserIsExpired (objMember, iMaxAge, iDaysForEmail, iRes)
	Dim intUserAccountControl, dtmValue, intTimeInterval
	Dim strName
	On Error Resume Next
	Err.Clear
	strName = Mid (objMember.Name, 4)
	intUserAccountControl = objMember.Get ("userAccountControl")

	If intUserAccountControl And ADS_UF_DONT_EXPIRE_PASSWD Then
		dp "The password for " &amp; strName &amp; " does not expire."
		UserIsExpired = False
	Else
		iRes = 0
		dtmValue = objMember.PasswordLastChanged
		If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then
			UserIsExpired = True
			dp "The password for " &amp; strName &amp; " has never been set."
		Else
			intTimeInterval = Int (Now - dtmValue)
			dp "The password for " &amp; strName &amp; " was last set on " &amp; _
			DateValue(dtmValue) &amp; " at " &amp; TimeValue(dtmValue) &amp; _
			" (" &amp; intTimeInterval &amp; " days ago)"
			If intTimeInterval &gt;= iMaxAge Then
				dp "The password for " &amp; strName &amp; " has expired."
				UserIsExpired = True
			Else
				iRes = Int ((dtmValue + iMaxAge) - Now)
				dp "The password for " &amp; strName &amp; " will expire on " &amp; _
				DateValue(dtmValue + iMaxAge) &amp; " (" &amp; _
				iRes &amp; " days from today)."
				If iRes &lt;= iDaysForEmail Then
					dp strName &amp; " needs an email for password change"
					UserIsExpired = True
				Else
					dp strName &amp; " does not need an email for password change"
					'Swap commented variable below to force email to be sent (for testing).
					UserIsExpired = False
					'UserIsExpired = True
				End If
			End If
		End If
	End If
End Function

Sub ProcessUser (iMaxPwdAge)
	Dim iResult, strExpire
	If Right (objMember.Name, 1) &lt;&gt; "$" Then
		If IsEmpty (objMember.Mail) or IsNull  (objMember.Mail) Then
			dp Mid (objMember.Name, 4) &amp; " has no mailbox"
		Else
			If UserIsExpired (objMember, iMaxPwdAge, DAYS_FOR_EMAIL, iResult) Then
				objFile.WriteLine "Sending an email to " &amp; objMember.givenName &amp; " " &amp; objMember.sn &amp; _
				" (" &amp; objMember.Mail &amp; ").  Password expires in " &amp; iResult &amp; " days." &amp; vbCRLF
				Call SendEmail (iResult)
			Else
				If iResult = "" Then
					strExpire = "."
				Else
					strExpire = " for " &amp; iResult &amp; " days."
				End If
				objFile.WriteLine "Skipping " &amp; objMember.givenName &amp; " " &amp; objMember.sn &amp; _
				". Password does not expire" &amp; strExpire &amp; vbCRLF
			End If
		End If
	End If
End Sub

Sub SendEmail (iResult)
	Dim objMail
	Set objMail = CreateObject ("CDO.Message")
	objMail.Configuration.Fields.Item ("<a href="http://schemas.microsoft.com/cdo/configuration/sendusing">http://schemas.microsoft.com/cdo/configuration/sendusing</a>")      = 2
	objMail.Configuration.Fields.Item ("<a href="http://schemas.microsoft.com/cdo/configuration/smtpserver">http://schemas.microsoft.com/cdo/configuration/smtpserver</a>")     = SMTP_SERVER
	objMail.Configuration.Fields.Item ("<a href="http://schemas.microsoft.com/cdo/configuration/smtpserverport">http://schemas.microsoft.com/cdo/configuration/smtpserverport</a>") = 25
	objMail.Configuration.Fields.Update

	objMail.From = STRFROM
	objMail.To = objMember.Mail

	objMail.Subject = "The Windows password is going to expire for " &amp; Mid (objMember.Name, 4)
	objMail.Textbody = "The Windows Active Directory password for user " &amp; objMember.givenName &amp; " " &amp; objMember.sn &amp; _
	" (" &amp; objMember.sAMAccountName &amp; ")" &amp; " will expire in " &amp; iResult &amp; " days. " &amp; vbCRLF &amp; vbCRLF &amp; _
	"Please use Outlook Web Access (<a href="https://www.company.com">https://www.company.com</a>) to change it before it expires." &amp; vbCRLF &amp; vbCRLF &amp; _
	"Thank you," &amp; vbCRLF &amp; _
	"Company Exchange Team" &amp; vbCRLF &amp; vbCRLF &amp; _
	"Note: You have received this email because you are a member of a group that is authorized to change your " &amp; _
	"password via Outlook Web Access since you do not log in to the corporate network.  If this no longer applies to you, " &amp; _
	"please notify the Company Email Team (SMTPAddress) so you can be removed from the group."

	objMail.Send
	Set objMail = Nothing
End Sub

Sub dp (str)
	If bDebug Then
		objFile.WriteLine str
	End If
End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/send-email-notification-for-password-expiration-to-remote-users/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

