Microsoft continues the move away from “legacy” API’s for Exchange Server and more into Web Services with the next version of Exchange.
The Exchange Developer team has posted details of their roadmap on their blog.
Microsoft continues the move away from “legacy” API’s for Exchange Server and more into Web Services with the next version of Exchange.
The Exchange Developer team has posted details of their roadmap on their blog.
Now available and in the "must download" list of software.
Microsoft® Remote Server Administration Tools enables IT administrators to remotely manage roles and features in Windows Server® 2008 from a computer running Windows Vista® with Service Pack 1 (SP1).
Download Link (x86) | Download Link (x64)
While you are out, pick up the HyperV admin console for Vista SP1 as well.
If you are running into environments and working on aspects of them such as Exchange, OCS, PKI, or other Active Directory integrated applications, you will encounter situations where you may need to update the AD schema. Of course, the first question is, what version am I at today?
Here’s a handy way to find out:
Query for the objectVersion attribute of the CN=Schema,CN=Configuration,DC=yourdomainhere of your Active Directory domain and compare the value to the table below.
I’ve provided links to the schema update information (where available), if you are curious as to what is updated in each.
| objectVersion | AD Schema Update |
| 13 | Windows 2000 Server |
| 30 | Windows Server 2003 ** |
| 31 | Windows Server 2003 R2 |
| 44 | Windows Server 2008 |
** The Windows Server 2003 upgrade from 2000 also adds the CN=Windows2003Update,CN=ForestUpdates,CN=Configuration,DC=yourdomainhere and sets its revision attribute to 9. This hasn’t been used again, yet.
Exchange Server saves the current version of its schema updates in an attribute of two locations.
For /forestprep operations, query the rangeUpper attribute of the CN=ms-Exch-Schema-Version-Pt,CN=Schema,CN=Configuration,DC=yourdomainhere object.
For /domainprep operations, query the Microsoft Exchange System Objects object in each domain for its objectVersion value.
The table below provides the number to version translation.
| Value | Exchange Version |
| 4406 | Exchange 2000 Server |
| 6936 | Exchange Server 2003 |
| 10628 | Exchange Server 2007 |
A post over at CNET this morning gives us an insight into the latest numbers in the messaging and collaboration space between Microsoft and IBM.
Microsoft says that in the last six months of 2007, more than 300 big companies, representing 2.8 million people, began switching from IBM software onto Exchange Server, Office and SharePoint Server.
…
Gartner analyst Matt Cain says that among enterprises with more than 100 users, Microsoft holds about 62 percent share, compared with about 26 percent for IBM.
"Microsoft has been picking up a percentage point or two of e-mail market share based on seats for the past few years, and we expect that to continue," Cain said in an e-mail interview.
All this while Microsoft has announced a new set of migration tools and services to assist customers moving from the IBM to the Microsoft platform.
On this episode of Fun with PowerShell, we dive into the realm of retrieving account information from various sources using the DirectoryEntry object. If you are familiar with the ADSI interfaces, this should give you a good idea of how to transfer knowledge from that medium to the PowerShell/.NET way of thinking.
All the code examples are based on work I’ve done today, but as always YMMV. There’s always a better, faster, more creative way to do coding, and I highly encourage that thinking.
So, what is a DirectoryEntry object? Simply an instance of that class, borrowed from the .NET framework. You can use a DirectoryEntry to access any of the ADSI service providers, so LDAP, IIS, NDS, and WinNT. I am going to touch on LDAP and WinNT for this article.
Let’s start with the basics. You have an Active Directory domain. You want to find all servers in that domain (those that have checked in, at least). First, you need a starting point for your search.
PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://dc=company,dc=com")
Could I specify a particular domain controller? Sure!
PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://domainctrl1.company.com:389")
What if we wanted to start further down the tree?
PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://ou=something,ou=somewhere,dc=company,dc=com")
or
PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://domainctrl1.company.com:389/ou=something,ou=somewhere,dc=company,dc=com")
Can we make it even shorter? If your workstation is a member of the domain you are looking for, then we can.
PS C:\> $ad = [ADSI]
This is very nifty. At the lowest level, we are just selecting a particular object in the directory. For the purposes of our search, this happens to be a root or OU level of the directory. You could pluck out a particular individual object and manipulate from there. Try it with your user account.
PS C:\> $user = New-Object DirectoryServices.DirectoryEntry("LDAP://cn=Dillon TenBrink,ou=geeks,dc=company,dc=com") | fl *
Now that we have our $ad variable, representing our search root, we need a searcher.
PS C:\> $searcher = New-Object DirectoryServices.DirectorySearcher($ad)
Once we have a searcher, we have a selection of settings to flip on it to execute our query.
PS C:\> $searcher
CacheResults : True
ClientTimeout : -00:00:01
PropertyNamesOnly : False
Filter : (objectClass=*)
PageSize : 0
PropertiesToLoad : {}
ReferralChasing : External
SearchScope : Subtree
ServerPageTimeLimit : -00:00:01
ServerTimeLimit : -00:00:01
SizeLimit : 0
SearchRoot : System.DirectoryServices.DirectoryEntry
Sort : System.DirectoryServices.SortOption
Asynchronous : False
Tombstone : False
AttributeScopeQuery :
DerefAlias : Never
SecurityMasks : None
ExtendedDN : None
DirectorySynchronization :
VirtualListView :
Site :
Container :
From here, we go to work defining our filter and the properties we would like to get back.
PS C:\> $searcher.Filter = "(&(objectClass=Computer)(operatingSystem=*Server*))"
PS C:\> $searcher.SizeLimit = 1000
PS C:\> $searcher.PropertiesToLoad.Add("cn")
PS C:\> $searcher.PropertiesToLoad.Add("operatingSystem")
You could define more options if you needed. Check the documentation on the DirectorySearcher class for more information.
From here you can execute the .FindOne() or .FindAll() methods to return the results of your query.
Let’s move on to a different animal using the DirectoryEntry class to show some versatility. This time we’ll use the WinNT ADSI interface.
The WinNT interface is still, in my opinion, the best way to get information on local accounts and groups remotely on a Windows box. Here we are going to use it to get a list of the local users on a server PDX01. First, like before, we create our DirectoryEntry.
PS C:\> $Sys = New-Object DirectoryServices.DirectoryEntry("WinNT://PDX01,computer")
If you performed a $Sys | fl * at this point, you can see all the properties of this object, just like your user account from AD above when we’re using the LDAP provider. What we’re after though, is the members, or "children", of this computer.
PS C:\> $Sys.children
Executing this command will return users, groups, and services. Since we’re just after the users, let’s filter it down.
PS C:\> $Sys.children | where {$_.SchemaClassName -match "User"}
Much better. Now we have our list of users. If we wanted to save this for later, dump it into a variable.
PS C:\> $Users = $Sys.children | where {$_.SchemaClassName -match "User"}
And then pipe all the details out to the console.
PS C:\> $Users | fl *
There you go.
From both of these starting blocks, you can then use your scripting prowess to harness the results of these queries to do your bidding. Take the AD server query, drop it into a loop with the local account query, and you have a quick and powerful local account audit script. Extend it a little more and take the local users, filter by their password age attribute and you have a functional password age checking script for local accounts. The possibilities are endless once you can retrieve the data sets.
Is this easier in vbScript or VB.NET?
My personal preference is to write these particular queries in VB.NET because I typically have more data analysis to do against other systems. Just my preference. That’s not to say I couldn’t write those interfaces and analysis in PowerShell; just that I haven’t found a need quite yet. The PowerShell way is very useful though, and quicker, when just plain, raw data is required and I use it frequently in those scenarios.
Benefits over vbScript? You can walk though this line by line, in real time, and adjust as you go to get your results exactly the way you want them. You wouldn’t want to write that script, execute, correct, and run again from the start over and over until you got what you wanted, right? PowerShell provides the advantage of having feedback line-by-line as you progress. Then, save your refined statements as a .ps1 file and reuse it later or call it in other scripts.
Happy scripting!