Script to Create an Active Directory Schema Update Report

by Jeremy Saunders on June 22, 2014

This PowerShell script was written by the awesome Ashley McGlone (AKA Goatee PFE) and published to the TechNet Script Center. It was also blogged on the Scriting Guy TechNet site. However, it did need some updates to keep up with the newer schema updates as well as adding the SCCM (ConfigMgr) versions. As Ashley has not updated it since 19th September 2013 I thought I’d update it myself and post it here until he gets the time to update the script and publish an updated version.

It will report the versions of the following products as per the sample screen shot from one of my customers:

  • Active Directory
  • Exchange
  • Lync
  • System Center Configuration Manager (SCCM) – ConfigMgr

Schema Version of Products

Please ensure you read the Scripting Guy’s blog titled “How to Find Active Directory Schema Update History by Using PowerShell“, which provides some great documentation on the script as well as some valuable comments that have helped to implement the updates.

The original script can be found here on the TechNet Script Center: PowerShell Active Directory Schema Update Report

Here is the Get-ADSchemaReport.ps1 script:

<#-----------------------------------------------------------------------------
How to find AD schema update history using PowerShell
Ashley McGlone, Microsoft Premier Field Engineer
http://blogs.technet.com/b/ashleymcglone
December, 2011

This script reports on schema update and version history for Active Directory.
It requires the ActiveDirectory module to run.
It makes no changes to the environment.

UPDATED:
2013-03-12  Added Windows Server 2012, Exchange 2010 SP3 & 2013, Lync 2013
2013-09-19  Added Windows Server 2012 R2
            Added OID for schema attributes
            Added schema.csv output
            Sorted output
2014-06-12  Added Exchange 2013 SP1 as documented in the comments on the Scripting Guy blog
            Added SCCM as documented in the comments on the Scripting Guy blog
            http://blogs.technet.com/b/heyscriptingguy/archive/2012/01/05/how-to-find-active-directory-schema-update-history-by-using-powershell.aspx
2014-06-24  Added Exchange 2013 cumulative update versions as per feedback
            http://supertekboy.com/2014/05/01/check-exchange-schema-objects-adsi-edit/
            http://www.bhargavs.com/index.php/2009/11/20/verify-exchange-server-schema-version/

References for schema values:
http://support.microsoft.com/kb/556086?wa=wsignin1.0
http://social.technet.microsoft.com/wiki/contents/articles/2772.exchange-schema-versions-common-questions-answers.aspx
http://technet.microsoft.com/en-us/library/gg412822.aspx

LEGAL DISCLAIMER
This Sample Code is provided for the purpose of illustration only and is not
intended to be used in a production environment.  THIS SAMPLE CODE AND ANY
RELATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.  We grant You a
nonexclusive, royalty-free right to use and modify the Sample Code and to
reproduce and distribute the object code form of the Sample Code, provided
that You agree: (i) to not use Our name, logo, or trademarks to market Your
software product in which the Sample Code is embedded; (ii) to include a valid
copyright notice on Your software product in which the Sample Code is embedded;
and (iii) to indemnify, hold harmless, and defend Us and Our suppliers from and
against any claims or lawsuits, including attorneys’ fees, that arise or result
from the use or distribution of the Sample Code.

This posting is provided "AS IS" with no warranties, and confers no rights. Use
of included script samples are subject to the terms specified
at http://www.microsoft.com/info/cpyright.htm.
-----------------------------------------------------------------------------#>

Import-Module ActiveDirectory 

$schema = Get-ADObject -SearchBase ((Get-ADRootDSE).schemaNamingContext) `
    -SearchScope OneLevel -Filter * -Property objectClass, name, whenChanged,`
    whenCreated, attributeID | Select-Object objectClass, attributeID, name,`
    whenCreated, whenChanged, `
    @{name="event";expression={($_.whenCreated).Date.ToString("yyyy-MM-dd")}} |
    Sort-Object event, objectClass, name

"`nDetails of schema objects created by date:"
$schema | Format-Table objectClass, attributeID, name, whenCreated, whenChanged `
    -GroupBy event -AutoSize

"`nCount of schema objects created by date:"
$schema | Group-Object event | Format-Table Count, Name, Group -AutoSize

$schema | Export-CSV .\schema.csv -NoTypeInformation
"`nSchema CSV output here: .\schema.csv"

#------------------------------------------------------------------------------

"`nForest domain creation dates:"
Get-ADObject -SearchBase (Get-ADForest).PartitionsContainer `
    -LDAPFilter "(&(objectClass=crossRef)(systemFlags=3))" `
    -Property dnsRoot, nETBIOSName, whenCreated |
  Sort-Object whenCreated |
  Format-Table dnsRoot, nETBIOSName, whenCreated -AutoSize

#------------------------------------------------------------------------------

$SchemaVersions = @()

$SchemaHashAD = @{
    13="Windows 2000 Server";
    30="Windows Server 2003 RTM";
    31="Windows Server 2003 R2";
    44="Windows Server 2008 RTM";
    47="Windows Server 2008 R2";
    56="Windows Server 2012 RTM";
    69="Windows Server 2012 R2"
    }

$SchemaPartition = (Get-ADRootDSE).NamingContexts | Where-Object {$_ -like "*Schema*"}
$SchemaVersionAD = (Get-ADObject $SchemaPartition -Property objectVersion).objectVersion
$SchemaVersions += 1 | Select-Object `
    @{name="Product";expression={"AD"}}, `
    @{name="Schema";expression={$SchemaVersionAD}}, `
    @{name="Version";expression={$SchemaHashAD.Item($SchemaVersionAD)}}

#------------------------------------------------------------------------------

$SchemaHashExchange = @{
    4397="Exchange Server 2000 RTM";
    4406="Exchange Server 2000 SP3";
    6870="Exchange Server 2003 RTM or SP1 or SP2";
    6936="Exchange Server 2003 SP3";
    10637="Exchange Server 2007 RTM";
    11116="Exchange 2007 SP1";
    14622="Exchange 2007 SP2 or Exchange 2010 RTM";
    14625="Exchange 2007 SP3";
    14726="Exchange 2010 SP1";
    14732="Exchange 2010 SP2";
    14734="Exchange 2010 SP3";
    15137="Exchange 2013 RTM";
    15254="Exchange 2013 CU1";
    15281="Exchange 2013 CU2";
    15283="Exchange 2013 CU3";
    15292="Exchange 2013 SP1"
    }

$SchemaPathExchange = "CN=ms-Exch-Schema-Version-Pt,$SchemaPartition"
If (Test-Path "AD:$SchemaPathExchange") {
    $SchemaVersionExchange = (Get-ADObject $SchemaPathExchange -Property rangeUpper).rangeUpper
} Else {
    $SchemaVersionExchange = 0
}

$SchemaVersions += 1 | Select-Object `
    @{name="Product";expression={"Exchange"}}, `
    @{name="Schema";expression={$SchemaVersionExchange}}, `
    @{name="Version";expression={$SchemaHashExchange.Item($SchemaVersionExchange)}}

#------------------------------------------------------------------------------

$SchemaHashLync = @{
    1006="LCS 2005";
    1007="OCS 2007 R1";
    1008="OCS 2007 R2";
    1100="Lync Server 2010";
    1150="Lync Server 2013"
    }

$SchemaPathLync = "CN=ms-RTC-SIP-SchemaVersion,$SchemaPartition"
If (Test-Path "AD:$SchemaPathLync") {
    $SchemaVersionLync = (Get-ADObject $SchemaPathLync -Property rangeUpper).rangeUpper
} Else {
    $SchemaVersionLync = 0
}

$SchemaVersions += 1 | Select-Object `
    @{name="Product";expression={"Lync"}}, `
    @{name="Schema";expression={$SchemaVersionLync}}, `
    @{name="Version";expression={$SchemaHashLync.Item($SchemaVersionLync)}}

#------------------------------------------------------------------------------

$SchemaHashSCCM = @{
    "4.00.5135.0000"="SCCM 2007 Beta 1";
    "4.00.5931.0000"="SCCM 2007 RTM";
    "4.00.6221.1000"="SCCM 2007 SP1/R2";
    "4.00.6221.1193"="SCCM 2007 SP1 (KB977203)";
    "4.00.6487.2000"="SCCM 2007 SP2";
    "4.00.6487.2111"="SCCM 2007 SP2 (KB977203)";
    "4.00.6487.2157"="SCCM 2007 R3";
    "4.00.6487.2207"="SCCM 2007 SP2 (KB2750782)";
    "5.00.7561.0000"="SCCM 2012 Beta 2";
    "5.00.7678.0000"="SCCM 2012 RC1";
    "5.00.7703.0000"="SCCM 2012 RC2";
    "5.00.7711.0000"="SCCM 2012 RTM";
    "5.00.7711.0200"="SCCM 2012 CU1";
    "5.00.7711.0301"="SCCM 2012 CU2";
    "5.00.7782.1000"="SCCM 2012 SP1 Beta";
    "5.00.7804.1000"="SCCM 2012 SP1";
    "5.00.7804.1202"="SCCM 2012 SP1 CU1";
    "5.00.7804.1300"="SCCM 2012 SP1 CU2";
    "5.00.7804.1400"="SCCM 2012 SP1 CU3";
    "5.00.7804.1500"="SCCM 2012 SP1 CU4";
    "5.00.7958.1000"="SCCM 2012 R2"
    }

$SchemaPathSCCM = "CN=System Management," + (Get-ADDomain).SystemsContainer
if (Test-Path "AD:$SchemaPathSCCM") {
  $SCCMData = Get-ADObject -SearchBase ("CN=System Management," + (Get-ADDomain).SystemsContainer) -LDAPFilter "(&(objectClass=mSSMSManagementPoint))" -Property mSSMSCapabilities,mSSMSMPName
  if ($sccmdata -isnot [system.Array]) {
    $SCCMxml = [XML]$SCCMdata.mSSMSCapabilities
    $schemaVersionSCCM = $SCCMxml.ClientOperationalSettings.Version
  } else {
    $schemaVersionSCCMList = Foreach($SCCMINstance in 0..($SCCMData.count -1)) {
      $SCCMxml = [XML]$SCCMdata[$SCCMInstance].mSSMSCapabilities
      $SCCMxml.ClientOperationalSettings.Version
      }
    $SchemaVersionSCCM = $schemaVersionSCCMList|Sort-Object -Descending|Select-Object -First 1
  }
} Else {
  $schemaVersionSCCM = 0
}

$SchemaVersions += 1 | Select-Object `
    @{name="Product";expression={"SCCM"}}, `
    @{name="Schema";expression={$schemaVersionSCCM}}, `
    @{name="Version";expression={$SchemaHashSCCM.Item($schemaVersionSCCM)}}

#------------------------------------------------------------------------------

"`nKnown current schema version of products:"
$SchemaVersions | Format-Table * -AutoSize

#---------------------------------------------------------------------------sdg

Enjoy!

Jeremy Saunders

Jeremy Saunders

Technical Architect | DevOps Evangelist | Software Developer | Microsoft, NVIDIA, Citrix and Desktop Virtualisation (VDI) Specialist/Expert | Rapper | Improvisor | Comedian | Property Investor | Kayaking enthusiast at J House Consulting
Jeremy Saunders is the Problem Terminator. He is a highly respected IT Professional with over 35 years’ experience in the industry. Using his exceptional design and problem solving skills with precise methodologies applied at both technical and business levels he is always focused on achieving the best business outcomes. He worked as an independent consultant until September 2017, when he took up a full time role at BHP, one of the largest and most innovative global mining companies. With a diverse skill set, high ethical standards, and attention to detail, coupled with a friendly nature and great sense of humour, Jeremy aligns to industry and vendor best practices, which puts him amongst the leaders of his field. He is intensely passionate about solving technology problems for his organisation, their customers and the tech community, to improve the user experience, reliability and operational support. Views and IP shared on this site belong to Jeremy.
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: