I guess most of you that are reading this post have been in a situation where you were dealing with some sort of ConfigMgr migration project. After objects have been migrated, you’re often faced with the dilemma of manually updating the content source locations for e.g. Applications and Packages. Today I’m proud to announce the release of my next tool aimed to speeding up any migration process by being able to match and replace portions or a simple server name in the content source location of any object type.

Download the tool

I’ve made the tool available on the TechNet Gallery.

Documentation

This tool is written in PowerShell and the minimum required version is 3. For updating the content source location of any Deployment Types for Applications, access to certain DLL’s is required. These files are available when the ConfigMgr console is installed on the system the tool is being executed on. For all other object types like e.g. Packages or Boot Images, there’re no requirements. You’re able to resize this tool to get a better view of the data grid view, simply just drag in any of the corners as you’d normally do.

When you first launch the tool, you’ll see that there are two text boxes called Match and Replace. The idea is to enter a match pattern in the Match text box that will be used by the tool for matching against the existing content source location of the select object type. As for the Replace text box, enter here what you want the Match pattern to be replaced with. If the pattern entered in the Match text box does not yield any results, the data grid view will not contain any information, meaning you’re not able to update any content. The tool is also capable of checking if there’s any associated content with the objects of the select object type. When for instance a match is found for an object, and the object has content associated, it will be added to the data grid view called Content Source information. You’re also able to select to copy the content source files from the existing location to the updated location for each object that the tool is instructed to process. If there’s any matched object in the data grid view that you do not wish to update, simply just remove the check mark for that object.

When running this tool, execute it elevated (with Administrative rights) on the Primary Site server where you want the objects content source location to be updated. It’s not been tested to run this tool remotely, although it should most likely run without any errors given that your system for fills the requirements.

Example of usage

Let’s pretend that you’ve migrated a couple of Packages from either a ConfigMgr 2012 or ConfigMgr 2007 source hierarchy called CM01 and only want to update a single package, for testing purposes. Your new Primary Site server is called CM02. Your existing content library on CM01 has been shared as:

\\CM01\Source$

All of the content for your Packages are stored in:

\\CM01\Source$\Pkgs

In this scenario, the Packages have been migrated to CM02 and you’d like to update the content source location to reflect the new server name. We’re assuming that you’ve copied all of the content source files with the same structure over to the CM02 server, sharing it with the same name like Source$.

  1. Execute the tool on your new Primary Site server called CM02 with elevated rights and by specifying the Primary Site server name for the SiteServer parameter.
    159_4
  2. In the Match text box, enter \\CM01\Source$. For the Replace text box, enter \\CM02\Source$.
  3. In the Options section, select Package as the desired object type.
  4. Click on Validate to start the validation process where the tool check if there’s any objects matching the pattern and that are eligible for a content source location update.
    159_2
  5. Once you’ve clicked on the Validate button, the tool connects to the SMS Provider and yields the eligible objects in the Content Source Information data grid view.
    159_3
  6. Deselect the objects that you don’t want to update and click on the Update button.
    159_5
  7. The tool will not start updating the two objects that have been selected. It will output in the Log Information text field if it was either successful or an error occurred.
    159_6

Now the two selected objects are updating and can be verified in the ConfigMgr console. I showed you this example as a way of verifying that it works in your environment. Once you’ve tested it, you should be good to go for running it on a broader scale.

 

Nickolaj Andersen
Principal Consultant and Enterprise Mobility MVP since 2016. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows devices and deployments including automation. Currently working for TrueSec as a Principal Consultant. Awarded as PowerShell Hero in 2015 by the community for his script and tools contributions. Creator of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService to name a few. Frequent speaker at conferences and user groups.

(2835)

comments
  • Rich
    Posted at 09:59 August 26, 2015
    Rich
    Reply
    Author

    As always great job!

    • Nickolaj
      Posted at 13:56 September 7, 2015
      Nickolaj
      Reply
      Author

      Thanks Rich! Much appreciated.

      Regards,
      Nickolaj

  • Michael
    Posted at 13:34 April 13, 2016
    Michael
    Reply
    Author

    Hi Nickolaj,

    i tried this tool connected to an SCCM 2012 R2 SP1 CU3 Site.
    With Applications there are no Problems.
    But at Packages, OS Images, Drivers and Driver packages i get this Errors even after sucessfull Validation:
    Exception calling “Put” with “0” argument(s): “Generic failure ”
    At S:\_TEMP\Update-ConfigMgrContentSourceGUI_1.0.0.ps1:350 char:37
    + $Package.Put() | Out-Null
    + ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Exception calling “Put” with “0” argument(s): “Generic failure ”
    At S:\_TEMP\Update-ConfigMgrContentSourceGUI_1.0.0.ps1:796 char:41
    + $OperatingSystemImage.Put() | Out-Null
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Can you help me here?

    Thanks
    Michael

    • Michael
      Posted at 16:12 April 13, 2016
      Michael
      Reply
      Author

      Hi Nickolaj,

      Problem solved my myself.
      I was connected to a child Primary site in a Hierarchy.
      After i connected to the CAS Site and run the tool again it works as expected 😉

      Michael

  • bmiller
    Posted at 22:41 May 17, 2016
    bmiller
    Reply
    Author

    Hi Nickolaj,

    Been trawling looking for something like this tool to assist with a migration. This looks exactly what I require but it only returns content if the DeploymentTypeName matches the title of the application. For some reason we decided that VBScript would be a suitable title for all but five of our DeploymentTypes.

    Will see if I can work around this to return all Applications irrespective of DeploymentTypeName.

    Thank you for your efforts with this tool.

    • bmiller
      Posted at 23:45 May 23, 2016
      bmiller
      Reply
      Author

      Decided due to time constraints that we would just run without the GUI and use your original script.

      Notes:
      Our environment was strange and we had to convert the location to lower case prior to updating.

      # Convert all to lower case
      $CurrentContentPath = $CurrentContentPath.ToLower()
      $UpdatedContentPath = $UpdatedContentPath.ToLower()

      Also added the option to comment out returning only one application via WMI for those not brave enough to Update all together.

      To be used when applying to only one application
      $applicationName = “Enter Application Name Here”

      # To be used when applying to only one application
      $Applications = Get-WmiObject -ComputerName $SiteServer -Namespace root\SMS\site_$SiteCode -class SMS_Application | Where-Object { $_.LocalizedDisplayName -eq $applicationName -and $_.isLatest -eq $True }

      # Selects all applications, ignores application name value
      # $Applications = Get-WmiObject -ComputerName $SiteServer -Namespace root\SMS\site_$SiteCode -class SMS_Application | Where-Object { $_.isLatest -eq $True }

      We also changed the hardcoding of the Fast/Slow PeerCache and PinOnClient to write back the same settings that were already there as we have applications over a certain size limited to download only on Fast networks.

      if ($SetEnumAnswer -like “Update”) {
      Write-Output “”
      $Applications | ForEach-Object {
      $Application = [wmi]$_.__PATH
      $ApplicationXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($Application.SDMPackageXML,$True)
      foreach ($DeploymentType in $ApplicationXML.DeploymentTypes) {
      $Installer = $DeploymentType.Installer
      $Contents = $Installer.Contents[0]

      #Convert to lowercase
      $Contents.Location = ($Contents.Location).ToLower()

      $UpdatePath = ($Contents.Location).Replace($CurrentContentPath,$UpdatedContentPath)

      if ($UpdatePath -ne $Contents.Location) {

      $UpdateContent = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentImporter]::CreateContentFromFolder($UpdatePath)
      $UpdateContent.FallbackToUnprotectedDP = $Contents.FallbackToUnprotectedDP
      $UpdateContent.OnFastNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::$($Contents.OnFastNetwork)
      $UpdateContent.OnSlowNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::$($Contents.OnSlowNetwork)
      $UpdateContent.PeerCache = $Contents.PeerCache
      $UpdateContent.PinOnClient = $Contents.PinOnClient

      $Installer.Contents[0].ID = $UpdateContent.ID
      $Installer.Contents[0] = $UpdateContent

      }
      }
      $UpdatedXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::SerializeToString($ApplicationXML, $True)
      $Application.SDMPackageXML = $UpdatedXML
      $Application.Put() | Out-Null
      Write-Output “INFO: Updated content path for $($_.LocalizedDisplayName)”
      }
      }

  • Martin
    Posted at 10:07 May 23, 2016
    Martin
    Reply
    Author

    Hi

    I’ve used this tool to update almost everything.. but it crashed at some point.. when restarting the GUI/script I get:
    “unable to determine sitecode”
    When running ” Get-WmiObject -Namespace “root\SMS” -Class SMS_ProviderLocation -ComputerName “ps2″”
    just to test connectivity I am seeing
    “Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)”

    even after reboot this happens..
    I’ve done nothing on the server….

    thoughts?

    • Martin
      Posted at 10:26 May 23, 2016
      Martin
      Reply
      Author

      delete above comment.. I was an idiot! siteservername not bloody sitecode!

      • Nickolaj
        Posted at 18:09 May 23, 2016
        Nickolaj
        Reply
        Author

        Hi Martin,

        Hehe the parameter SiteServer should (hopefully) be clear enough not to specify the Site Code 😉

        Regards,
        Nickolaj

  • Martin
    Posted at 07:08 May 24, 2016
    Martin
    Reply
    Author

    Hi
    yes, Thx.. think I was just in caffeine-withdrawal.. as I wrote it wasn’t like it was the first time I tried using the tool 😐

  • Jesper Lund Walsted
    Posted at 09:24 August 23, 2016
    Jesper Lund Walsted
    Reply
    Author

    This is awesome. Thank you.
    If I am allowed to comment, a “Delete original source after copy” would be sweet.

  • Greg Marcoux
    Posted at 20:23 October 25, 2016
    Greg Marcoux
    Reply
    Author

    Nice work, so helpful…so much time saved.

  • Marc Pichel
    Posted at 11:45 November 17, 2016
    Marc Pichel
    Reply
    Author

    Very very useful tool, thank you kindly for contributing.

  • Jitesh Patel
    Posted at 11:16 December 13, 2016
    Jitesh Patel
    Reply
    Author

    Thank you very much Nickolaj – you’ve literally saved us a days worth of work!

  • jac3d
    Posted at 15:05 December 23, 2016
    jac3d
    Reply
    Author

    This is great! Is there a way do de-select the checkbox on each application? I have over 400 applications and would rather do a few at a time and unchecking all will take forever. If not where would i reverse the check mark in the script?

  • Akhilesh Yadav
    Posted at 11:15 February 8, 2017
    Akhilesh Yadav
    Reply
    Author

    Can we use it for resolving issue related validation failed error while distributing to DP.

  • Nik
    Posted at 11:54 February 14, 2017
    Nik
    Reply
    Author

    It’s brilliant. Thank you.

    • Nik
      Posted at 10:24 February 15, 2017
      Nik
      Reply
      Author

      I had copied, half-copied and un-copied source files on new server. Robocopy seems a nicer option than Copy-Item and you can limit network usage if necessary.

      Line 216 ..
      if (Test-Path -Path $ExistingContentLocation) {
      START-PROCESS ROBOCOPY -ArgumentList “$ExistingContentLocation $AmendUpdatedContentLocation /MIR” -wait

      }
      else {
      Write-OutputBox -OutputBoxMessage “Unable to access $($ExistingContentLocation)” -Type WARNING
      }
      }
      else {
      if (-not(Test-Path -Path $UpdatedContentLocation)) {
      New-Item -Path $UpdatedContentLocation -ItemType Directory | Out-Null
      }
      if (Test-Path -Path $ExistingContentLocation) {
      START-PROCESS ROBOCOPY -ArgumentList “$ExistingContentLocation $UpdatedContentLocation /MIR” -wait

      }

  • nik
    Posted at 19:32 February 15, 2017
    nik
    Reply
    Author

    But the “/” at the end of each path causes a problem when the are spaces in destination so got rid of them all

    Line 206
    $ExistingContentLocation = $DeploymentType.Installer.Contents[0].Location.trimend(“\”)

    Line 208
    $UpdatedContentLocation = $DeploymentType.Installer.Contents[0].Location.trimend(“\”) -replace [regex]::Escape($TextBoxMatch.Text), $TextBoxReplace.Text

    Perhaps just include my first comment 🙂 It is brilliant.

  • Chris
    Posted at 11:14 June 26, 2017
    Chris
    Reply
    Author

    many thanks, just have changed ~500 packages with a single click 🙂

  • Leave a Reply to Rich
    Cancel Reply