Run scripts before the ‘Format Disk’ step in your SCCM OSD Task Sequence using a vdisk


You’ve got your SCCM OSD Task Sequence ready/working but you want to run some scripts in the WinPE phase before the Format Disk steps.

This will generally work well if there is a formatted disk available as you can just reference your script in a package like you normally would, and the package content will be downloaded to the disk.

But what if the disk is not formatted or BitLocker encrypted or is not writable for some other reason?

Simple, just run them from a network share right? Like most people do. How about if you have 100’s or even 1000’s or remote locations with poor WAN connectivity and no local file servers/NAS etc.?

Well, look no further, the solution is herein detailed.

Existing Solution

One of the most commonly used solutions out there right now is to run scripts directly from a UNC path (make sure you authenticate first):

Running scripts from the network without package content download

New vdisk Solution

The new solution creates a temporary vhdx file in the RAM drive that WinPE is using. This is an NTFS formatted virtual disk and can be used to download package content when there is no physical disk available because it first needs formatting.

You may ask, why not just format the disk first and run the script? Well if you run some pre-checks and find the machine is not suitable/supported for building and want to revert back to the currently installed OS, then formatting it would not be a great thing to do.

Temp Local Disk group

Create a condition on the group so that it only creates the vdisk if no local disk is available – this condition is optional, there is no harm in creating the vdisk always, as it gets cleaned up shortly after anyway.

Run Command Line – Create Temp vDisk DiskPart File

In the WinPE phase, right at the beginning of the Task Sequence create a Run Command Line step with the following code (adjust the vdisk size according to the amount of content you need to download before the format disk step using maximum=xxx).

Note: The space available in WinPE RAM drive is adjustable in the boot image properties.

cmd.exe /c echo create vdisk file="%temp%\temp.vhdx" maximum=100 >> %temp%\diskpartvDisk.txt && cmd.exe /c echo select vdisk file="%temp%\temp.vhdx" >> %temp%\diskpartvDisk.txt && cmd.exe /c echo attach vdisk >> %temp%\diskpartvDisk.txt && cmd.exe /c echo create part primary >> %temp%\diskpartvDisk.txt && cmd.exe /c echo format fs=ntfs label="Temp Vol" quick >> %temp%\diskpartvDisk.txt && cmd.exe /c echo assign >> %temp%\diskpartvDisk.txt

Run Command Line – Run DiskPart vDisk File

Run diskpart.exe against the file created in the previous step

diskpart.exe /s %temp%\diskpartvDisk.txt

Use package content

Run any script/file, whatever you want, using package content

vDisk Cleanup Group

Has the following condition

Run Command Line – Create DiskPart File to del vDisk

Run the following script to create a diskpart file that will cleanup the existing vdisk

cmd.exe /c echo list volume> %temp%\diskpartDelvDiskList.txt & for /f "tokens=1-4" %a in ('diskpart /s %temp%\diskpartDelvDiskList.txt ^| find "Temp Vol"') do echo select %a %b> %temp%\diskpartDelvDisk.txt & echo clean>> %temp%\diskpartDelvDisk.txt & echo offline disk>> %temp%\diskpartDelvDisk.txt

Run Command Line – Run DiskPart Del vDisk File

diskpart.exe /s %temp%\diskpartDelvDisk.txt

So that’s it, the OSD Task Sequence can continue with the format disk steps following the cleanup of the vdisk.


Simple SQL Maintenance for your SCCM / ConfigMgr environment – PowerShell CI

I took this idea from one of my previous posts where a PowerShell script can be loaded into an SCCM Configuration Item and run automagically across all the SCCM servers using a dynamic collection. Hands off and completely automated.

This time round it’s the renowned Ola Hallengren SQL maintenance scripts. Ola’s scripts have always been recommended by ConfigMgr/SCCM MVPs and partners etc. The re-indexing provides performance gains in the CM database. SQL databases need maintenance as over time, the data/tables/indexes can become fragmented and this leads to poor performance.

You can check for SQL fragmentation using this T-SQL:


This will give you a list of the databases and mostly importantly, the dbid. The ConfigMgr database in this example has ID = 7

select * from sys.dm_db_index_physical_stats (7,DEFAULT,DEFAULT,DEFAULT,DEFAULT)
where page_count > 1000
order by avg_fragmentation_in_percent desc

This will give you the avg_fragmentation_in_percent column. You can use this to compare before/after running the Ola Hallengren maintenance scripts.

In an SCCM hierarchy, each CAS and Primary Site will need its own database. These are generally run on a single SQL server per SCCM site. Software Update Points also use SQL databases and can sometimes share the SQL Server with the SCCM Primary Site Server. SUPs (WSUS) can also share databases.

I have developed a simple PowerShell script that uses an SCCM Configuration Item (CI) /Baseline to deploy the Ola Hallengren SQL maintenance onto the SCCM/SUP SQL Servers and maintain the SQL Agent scheduling for the various maintenance tasks. It is effectively a DSC/Compliance for the Agent Jobs/Schedules.

The CI is targetted at a dynamic collection containing all SCCM SQL servers and SUPs.


  • The SYSTEM account on each of the SQL / WSUS servers has sysadmin access to the SQL server
  • SQL is installed locally on the WSUS/SUP server (if this is not the case, just change the collection query appropriately)

Script Overview

The script does the following:

  • Retrieves the Ola Hallengren MaintenanceSolution.sql from the disk or network
  • Loads the SQL Server PoSh module from the disk or network
  • Writes to a local CMTrace formatted log file
  • Installs MaintenanceSolution.sql into the Master database
  • Creates SQL Agents Jobs
  • Changes the SQL Agent service startup to automatic
  • Sets SQL Agent Job schedules
  • Upgrades the MaintenanceSolution.sql scripts if it detects a new version available on the disk or network
  • Supports adding custom TSQL to the job steps
  • Tested with SCCM 1802+ and SQL 2012+



  • Disable all existing SQL maintenance jobs but not backup jobs.
    Disable Jobs that are related to: DBCC/Integrity Checks/Indexing
  • Download a copy of my SCCM SQL Ola Hallengren Maintenance Scripts.ps1.
  • Download the latest copy of MaintenanceSolution.sql
  • Copy the MaintenanceSolution.sql file to a network share that the computer accounts of the SCCM SQL Server and SUP Servers can access – this is the recommended approach so that all SQL servers can access the same version.
  • Grab a copy of the SQLServer module from the PowerShell gallery and copy to a network share. Same permissions as mentioned above.

Script Preparation

In the downloaded copy of SCCM SQL Ola Hallengren Maintenance Scripts.ps1, amend line 32 variable:

$OlaHallengrenScriptLocation = 

so that it reflects the UNC path of the MaintenanceSolution.sql file.

Also confirm that line 37 variable:

$SQLServerPoShModule =

points to a location that contains the SQLServer module file SqlServer.psm1 and that the location is also accessible by the computer accounts of the SCCM SQL Server and SUP Servers.

Adjust the schedules to your liking:


This variable contains all the SQL Agent jobs you want to configure. In my script they are mostly the Ola Hallengren ones. You can add your own custom jobs if they need configuring.

Note: The script does assume that the job has already been created by another means – in this case, by the MaintenanceSolution.sql script, so you’re wanting to configure your own SQL Agent jobs using this script, make sure they already exist.

Script Deployment

Add the contents of SCCM SQL Ola Hallengren Maintenance Scripts.ps1 to the Discovery script on a new Configuration Item in SCCM:

Add a dummy compliance rule:

Add the CI to a new Baseline and deploy the baseline to your SCCM SQL/WSUS servers collection. I used the below two queries for my collection:

select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.SystemRoles = "SMS SQL Server"
select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.SystemRoles = "SMS Software Update Point"


Not much to look at, but from the SSMS we can see the agent jobs and the schedules have been configured in accordance with the settings in our CI script:

Your SQL databases both SYSTEM and USER databases will now be optimised using Ola’s maintenance scripts.

Monitor the jobs in your test environment prior to production deployment to evaluate performance impact and competition times. The IndexOptimise job will take significantly longer during the first run if no maintenance has been performed on the databases before.

Office 365 ProPlus ODT setup.exe error 30050-1039

I couldn’t find this error blogged anywhere else at the time, so thought I’d do a quick post on it.

When using the click-to-run C2R installer setup.ex from the Office Deployment Tool ODT, I was getting error 30050-1039:

office proplus error

This error seemed to appear because of the RemoveMSI property in the ODT Configuration.xml:

<Add OfficeClientEdition=”32″ Channel=”Broad” OfficeMgmtCOM=”TRUE”>
<Product ID=”O365ProPlusRetail”>
<Language ID=”en-us” />
<ExcludeApp ID=”Groove” />
<RemoveMSI All=”True” />
<Property Name=”FORCEAPPSHUTDOWN” Value=”True”/>
<Property Name=”SharedComputerLicensing” Value=”0″ />
<Property Name=”PinIconsToTaskbar” Value=”FALSE” />
<Display Level=”None” AcceptEULA=”TRUE” />
<Logging Level=”Standard” Path=”C:\Temp”/>

In the Office 365 ODT logs I was getting errors like:

MSI product uninstallation failed with exit code\”,\”productId\”:\”Office16.VISSTD\”,\”exitCode\”:\”30066\”

So it was clear from this it wasn’t able to uninstall my Visio 2016 Standard installation. I wasn’t sure if the packaging team had created un-supported MSI based modification to the Visio Std package, but I didn’t want to use the OffScrubC2R.vbs to workaround the error, I wanted to try and detect and repair the issue on multiple machines in advance.

In the c:\windows\temp\SetupExe(201*).log I was getting this error:

[20160] Error: The setup.xml file at: C:\Program Files (x86)\Common Files\Microsoft Shared\OFFICE16\Office Setup Controller\Visio.en-us\setup.xml does not exist but is referenced in the ARP entry, therefore transition to MMode is unsafe: VISSTD Type: 27::InstalledProductStateCorrupt.

Strange, the Visio.en-us folder didn’t exist. I copied the “C:\Program Files (x86)\Common Files\Microsoft Shared\OFFICE16\Office Setup Controller\VISSTD” folder and renamed it to Visio.en-us and re-ran the ODT setup.exe.

Next error in the SetupExe(201*).log:

Error: The install state of the ProductCode {90160000-0054-0409-0000-0000000FF1CE}, which is referenced in the ProductCodes registry value, is not INSTALLSTATE_DEFAULT. The install state state is -1. Therefore transition to MMode is unsafe for product: VISSTD Type: 27::InstalledProductStateCorrupt.

Checking the Registry key: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Office16.VISSTD I could see the {90160000-0054-0409-0000-0000000FF1CE} ProductCode was listed:

O365 proplus install 3

I removed it from the list. Now you may think this would effect the uninstall of this MSI (which in my case was the core Visio install), but it didn’t.

I re-ran the ODT setup.exe and the previous Office install (Office 2016 Professional MSI edition) was completely uninstalled without error, finally.

Simple WSUS Maintenance/Cleanup for your SCCM environment


WSUS Cleanup in an SCCM has been done before – many times but I wanted a solution that focussed purely on the built-in WSUS cleanup options and something that was simple to deploy in any SCCM environment – using an SCCM Configuration Item.

WSUS Cleanup – SCCM built-in maintenance

Windows Server Updates Services (WSUS) is the underlying service used by System Center Configuration Manager (SCCM) to perform software updates (Windows patches for software and OS) on Windows servers and workstations.

The database that WSUS uses to store metadata about all the available updates has become very large over the years, covering more and more Microsoft operating systems and software. It is highly recommended by Microsoft to perform regular WSUS database maintenance as suggested here: Complete guide to Microsoft WSUS and Configuration Manager SUP maintenance.

When WSUS is used in conjunction with SCCM, SCCM can perform some automated cleanup tasks after each WSUS sync job has completed. In SCCM 1806 this cleanup is further improved and documented here: Software Updates Maintenance. Specifically:

Starting in version 1806, the WSUS cleanup option occurs after every sync and does the following cleanup items:

The Expired updates option for WSUS servers on CAS and primary sites.

  • WSUS servers for secondary sites, don’t run WSUS cleanup for expired updates.

Configuration Manager builds a list of superseded updates from its database. The list is based on the supersedence behavior in the Software Update Point component properties.

  • The update configuration items meeting the supersedence behavior criteria are expired in the Configuration Manager console.
  • The updates are declined in WSUS for CAS and primary sites but not for secondary sites.

A cleanup for software update configuration items in the Configuration Manager database occurs every seven days and removes unneeded updates from the console.

  • This cleanup won’t remove expired updates from the Configuration Manager console if they’re currently deployed.

The document then goes on to state:

The following WSUS Server Cleanup Wizard options aren’t run on the CAS and primary sites:

  • Unused updates and update revisions
  • Computers not contacting the server
  • Unneeded update files

The WSUS Cleanup option in SCCM is exposed here (in the configuration of the root SCCM sites’ SUP properties) :
WSUS Cleanup

Unfortunately what the Microsoft documentation does not cover is the order in which to run the cleanup jobs and on which level of the WSUS hierarchy.

In an SCCM hierarchy there are usually multiple tiers of WSUS:

  1. A single root WSUS (The top tier/level) which updates from the internet.
  2. Multiple lower level WSUS servers that sync with the root level WSUS.

Note: WSUS servers can also share the same SQL database so the maintenance would only need to be run once per database, rather than once per WSUS server, not that it would harm anything by running it multiple times per database.

There are comments from those in the know that certain WSUS cleanup tasks recommended-order-for-running-cleanup-wizard-in-wsus-upstream-replica-hierarchy should only be run from the top level WSUS down, so to clarify for sure, I went ahead to see how the SCCM team had implemented WSUS cleanup in a hierarchy by analysing the wsyncmgr.log:

  1. The CAS server (*VM01*) initiates a sync on the top level WSUS – (*565):
  2. Once the WSUS sync with the Microsoft Update catalog is done, SCCM sync’s it’s database with WSUS:
  3. Cleanup is then performed on the top tier WSUS. Cleanup in this case is marking updates as ‘declined’ – this is important:
  4. At this stage, nothing has happened with the lower tier WSUS servers. The CAS now updates the Primary Sites inboxes, requesting a WSUS Sync to the top tier WSUS:
  5. So far all the above snippets are from the CAS. Now we jump to a Primary Site server. We can see the Sync has started, 1 minute after the sync and cleanup has finished on the CAS.
  6. The Sync on the Primary site completes and run its own cleanup:

So above we can see that SCCM is processing the ‘decline updates’ part of the cleanup in the correct order – from the TOP WSUS, down.

Now to process the remaining WSUS cleanup options that Microsoft have stated SCCM doesn’t do:

  • Unused updates and update revisions
  • Computers not contacting the server
  • Unneeded update files

The Custom Cleanup script

I have taken some Powershell from @Bdam55 to cover the above three WSUS cleanup tasks and converted it to run as an SCCM Configuration Item on a schedule:

SCCM WSUS Cleanup script on Github

The actual cleanup of WSUS is nothing new, the PowerShell used here is regularly available on the internet, the cool thing is that this is a CI and is running in the SYSTEM context.

The script is designed to run against WSUS in an SCCM 1806 and above environment, as the additional WSUS Cleanup rules were introduced in this version.

Here are the benefits of this deployment method of the cleanup script, over say something like Group Policy Scheduled Tasks or BigFix scheduled script:

  • A Configuration Baseline can be targeted at a dynamic device collection containing all the SCCM site servers in the hierarchy.
  • All SCCM infrastructure maintenance is kept and controlled within SCCM.
  • No credentials are required in the script as it runs as SYSTEM on the SCCM Site Server
  • Uses the ConfigMgr PoSh module on the Site Server and then passes variables to the remote WSUS

When the script runs on the SCCM Site Server, it actually connects to each of the SUP (WSUS) servers in the site using PowerShell remoting (invoke-command) and initiates a cleanup if a sync job is no running. If the script detects it’s running on the root WSUS server, it will add a large delay in processing the cleanup, to give the lower level WSUS cleanup jobs time to finish.

Top level WSUS detection:

$TopTierWsus = Get-CMSoftwareUpdateSyncStatus | Where-Object -FilterScript {$_.WSUSSourceServer -like "*Microsoft Update*" -and $_.SiteCode -eq $SiteCode} | Select-Object -Unique -ExpandProperty WSUSServerName&amp;lt;span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			&amp;gt;&amp;lt;/span&amp;gt;

As per the script now also configures the minimum values for:

  • WSUS App Pool queue length
  • WSUS App Pool memory size
# WSUS IIS AppPool Variables
$WSUSSiteNameFilter = "WSUS*"
$IISAppPoolQueueMinSize = 2000
$IISAppPoolMemoryMinSize = 4194304
$ISSWebAdminModuleNAme = "WebAdministration"

$IISModule = $true
If(-not(get-module -Name $ISSWebAdminModuleNAme)){
        Import-Module -Name $ISSWebAdminModuleNAme
    } Catch {
        $IISModule = $false


    $WebSite = Get-Website | Where-Object -FilterScript {$_.Name -like $WSUSSiteNameFilter}
    If(-not($WebSite)){Add-TextToCMLog -LogFile $LogFile -Value "Could not find IIS website using filter '$WSUSSiteNameFilter'. Skipping IIS config" -Component $component -Severity 3}

    $WSUSAppPoolName = $WebSite.applicationPool

    $AppPool = Get-ItemProperty -Path IIS:\AppPools\$WSUSAppPoolName

    # WSUS App Pool Queue Length
    $AppPoolQueueLength = $AppPool.queueLength

    If($AppPoolQueueLength -lt $IISAppPoolQueueMinSize){
        Add-TextToCMLog -LogFile $LogFile -Value "Setting $WSUSAppPoolName IIS App Pool length to $IISAppPoolQueueMinSize" -Component $component -Severity 1
        Set-ItemProperty -Path $AppPool.PSPath -Name queueLength -Value $IISAppPoolQueueMinSize
    } else {
        Add-TextToCMLog -LogFile $LogFile -Value "$WSUSAppPoolName IIS App Pool length is $AppPoolQueueLength so is already above $IISAppPoolQueueMinSize" -Component $component -Severity 1

    # WSUS App Pool Private Memory Size
    $AppPoolMemorySize = (Get-ItemProperty -Path $AppPool.PSPath -Name recycling.periodicrestart.privateMemory).Value

    If($AppPoolMemorySize -lt $IISAppPoolMemoryMinSize){
        Add-TextToCMLog -LogFile $LogFile -Value "Setting $WSUSAppPoolName IIS App Pool memory size to $IISAppPoolMemoryMinSize" -Component $component -Severity 1
        Set-ItemProperty -Path $AppPool.PSPath -Name recycling.periodicrestart.privateMemory -Value $IISAppPoolMemoryMinSize
    } else {
        Add-TextToCMLog -LogFile $LogFile -Value "$WSUSAppPoolName IIS App Pool memory is $AppPoolMemorySize so is already above $IISAppPoolMemoryMinSize" -Component $component -Severity 1

    Add-TextToCMLog -LogFile $LogFile -Value "Could not load module $ISSWebAdminModuleNAme. Skipping IIS config" -Component $component -Severity 3
&amp;lt;span id="mce_SELREST_start" style="overflow: hidden; line-height: 0;"&amp;gt;&amp;lt;/span&amp;gt;

If the SCCM hierarchy is expanded to cope with additional load, or new servers are brought online to do a side-by-side migration, the Configuration Item should kick-in and cover these off automatically.

Thanks to: @Bdam55 for the Add-TextToCMLog function and most of the WSUS cleanup script.

Any questions, feel free to reach out.


Domain name registration transfer to Azure App Service domains

Updated 03/11/2018 [ A transfer-in of a .uk domain into Azure is not currently supported as the IPSTAG is required by Nominet on the existing provider side. I assume it would be GODADDY when transferring into Azure. The Azure portal will be updated soon to support UI based migrations ]


If you have some domain names registered with say 123-reg or another provider and want to migrate/transfer the ownership into Azure, you can do this with the supported top level domains: com, net,, org, nl, in, biz,, and (as documented here: Buy a custom domain name for Azure Web Apps.


Some of the reasons you might want to do this:

  • Take advantage of Microsoft Azure’s flat rate pricing, for all domains, that they have agreed with GoDaddy
  • Single console to control domains, DNS, traffic manager, web sites (app service) etc. etc.
  • Better automation/api functionality (in my opinion) than what some of the domain name hosting companies offer.


There are a few blogs on the internet on how to achieve this with PowerShell using:

New-AzureRmResource -ResourceType Microsoft.DomainRegistration/domains

like on Jos Liebens site.


However, like others had commented, I also received this error back after running appropriate PoSh:

New-AzureRmResource : {"Code":"BadRequest","Message":"Parameter domain is null or empty.","Target":null,"Details":[{"Message":"Parameter domain is null or empty."},{"Code":"BadRequest"},{"ErrorEntity":{"ExtendedCode":"51011″,"MessageTemplate":"Parameter {0} is null or empty.","Parameters":["domain"],"Code":"BadRequest","Message":"Parameter domain is nullor empty."}}],"Innererror":null}

So seems there maybe a bug with this AzureRM cmdlet? I couldn’t see this domain property mentioned in the Microsoft.DomainRegistration/domains documentation.


The Microsoft Azure REST API.

There are probably other ways to initiate a domain name transfer into Azure using the REST API, but I found this way to be pretty simple.

    1. Go to the Domains – Create Or Update page where you interact with the API from the Microsoft docs page.
    2. Click on the ‘Try it‘ button and login with your Azure AD credentials. ( I have global admin permissions in my tenant ).
    3. Add the mandatory parameters:
      resourceGroupName – where the App Service object will be created
      domainName – the domain name you are migrating from another provider into Azure
      api-version – I left this as defaultdomain_transfer_params
    4. For a domain transfer, I used the following body:
      Note: some of the properties are mandatory/required

       location: "Global",
       properties: {
        contactAdmin: "Jack Rudlin",
        contactBilling: "Jack Rudlin",
        contactRegistrant: "Jack Rudlin",
        contactTech: "Jack Rudlin",
        privacy: "True",
        autoRenew: "True",
        authCode: "q\\1u{b=wbY9bNT193iNS",
        Consent: {
         agreedAt: "2018-10-21T20:10:40",
         agreedBy: "",
         agreementKeys: ["DNPA","DNTA"]


      You should get a 202 response back if the post was successful

      Note: Don’t forget to escape your JSON! Check the authCode. I had a backslash \ in mine so I had to escape it with an additional \


    1. In the Azure Resource Group that you specified in the earlier parameters, the App Service should be listed with the domain name you are transferring:rg


    1. A day or two later, the annual charge for the domain hosting service should be taken from your Azure funds:azure domain cost


  1. Finally once the domain transfer has been successfully completed, you will get access to manage the domains DNS:
  2. Post domain transfer you’ll probably want to migrate you DNS and then web services.

I quite liked using the REST API post method from the browser. In an enterprise environment, I can immediately see these benefits:

  • Browser supports authenticated proxies natively  – PowerShell has issues with this
  • No need to download/install modules for PowerShell
  • No local administrator rights required
  • I guess the Azure cloud shell is similar, but that requires a storage account and has an additional cost association

Automatically document your SCCM infrastructure with a Visio diagram using PowerShell

Wow that’s a long title. To summarise:
PowerShell script = SCCM Visio diagram

If you have many different SCCM hierarchies in different AD domains and forests to manage, keeping relevant/up to date infrastructure diagrams can be time consuming, especially if there are lots of SCCM site systems, Visio can become painful to use.

I have put this script together: SCCM Visio diagram.ps1 with the help of colleague Ben Jones (especially for the maths parts) that automatically creates a Microsoft Visio diagram of your SCCM / ConfigMgr hierarchy.

I know there are tools like the CMMap / SMSMap ones, but the SMSMap one in particular is using older shapes, has not been tested with SCCM 2012+ or Visio 2013+. I noticed it was also creating duplicate servers for each role! which is a fairly big overhead when you have 100’s of roles.

I wanted to see an open source script that could be used on a schedule to automate the diagram creation. Some further design requirements:

  • User running script needs read only access to the SCCM reporting point. No SQL or SMS Provider/WMI access is required!
  • Only HTTP/HTTPS ports are used by the script
  • Shapes can easily be swapped out for a new stencil set if required
  • Diagram scales from huge to tiny SCCM environments
  • Custom shape data can be added to each object from an additional external source (like a corporate CMDB API)

Pre-reqs before running the script:

  • Microsoft Office Visio 2016 Standard or Professional must be installed on the machine from where the script is run. (Sorry, I know a license is required – I haven’t tested with the trial version)
  • Download and extract SCCM Visio stencils and then adjust line 12:
    # Custom SCCM Stencils used for building the infrastructure diagram
    $SCCM_Servers_Stencil_Path="\\domain.local\shares\files\visio\it\ConfigMgr_1610_Visio_Stencils_v1.3\ConfigMgr 1610 (Servers).vss"
  • Network access to the HTTP/HTTPS port of the top level SCCM Reporting services point site
  • SCCM Reporting Reader rights with access to these two reports:
$SCCM_SiteRoles_ReportName = 'Site system roles and site system servers for a specific site'
$SCCM_SiteStatus_ReportName = 'Site status for the hierarchy'
  • Adjust the following variable on line 9 to the FQDN of your top level SCCM reporting server:
$SCCM_SSRS_FQHostname = "SCCM-RP.domain.local"; # Central Administration Site reporting point

Some quick notes on the workings of the script
  • It’s using the Visio.Application COM object. You can use the developer mode in Visio to find out more about VBA
  • It has only been tested with en-gb language code 2057 in Visio. Try amending line 657 ($visCustPropsLangID) for diff languages.
  • The US version of SCCM Reporting Point was used. The names of the two reports mentioned above could be adjusted
  • Lines 726-745 are for when you want to use a custom API to retrieve additional info about your servers and add it the Visio shape data. I got lazy and didn’t add an optional variable to run this or not so you’ll probably want to remove this section to avoid errors at the end of the script.

Zoomed out to 30% in Visio – the whole overview. 5 Primary sites and a CAS.SCCM Visio Diagram 1

Zoomed to about 70% so you can see each site system has the roles, IP and server name as the text box description below the shape:SCCM Visio Diagram 2

A short YouTube video of how things look as Visio is processing the shapes passed from the PowerShell script: Automatic Visio diagram video

Thanks to Jean-Sébastien DUCHÊNE for the cool SCCM Visio stencils.

And again, huge thanks to Ben Jones for his mathematical skills and converting these to .net/PowerShell to get the correct spacing for each shape.

Any questions please ping me on Twitter @JackRudlin

SCCM Technical Preview 1808 – Repair app from Software Center

A quick overview of the new “repair” application feature in SCCM 1808 Tech Preview.

Quick post today. I just wanted to show you the new Repair Application feature from SCCM 1808’s Software Center.

In the previous TP release, the repair option was available in the Deployment Type of the Application but was not exposed in the Software Center UI. Now in the new SCCM 1808 Technical Preview we can use this new option from the Software Center.

The agent version of the machine running the repair option from Software Center should be at least. The site version should be 1808+ which is also

So lets get started and see how this new feature is implemented from the SCCM Console and how the UX is from the users perspective in the Software Center.

  1. Update your existing Application, or create a new one. Add a repair program in.

    You can usually find the ModifyPath of a an existing installed app from the Registry: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
    However, beware this key represents a ‘modify’, so it would probably be interactive and may not be suitable for your silent repair.

    SCCM Repair app 2

  2. Deploy your Application that has the repair option. You can only deploy to a User collection. Tick the box to “Allow end users to attempt to repair this application

    SCCM Repair app 1

  3. Jump onto your client as the user who you deployed the app to and install the deployed app. Once installed you will then see the new Repair option available.

    SCCM Repair app 3

  4. Run the new Repair option to see it in action! The user will get a prompt with this warning:

    SCCM Repair app 4

  5. Once the repair has finished, we can see from the AppEnforce.log that the repair cmd has successfully run:

    SCCM Repair app 5

  6. Thanks, and enjoy. I told you it was a quick one