Adobe Update Server Sync Issues

I was setting up an Adobe Update Server using the Adobe Update Server Setup Tool, and it was just not working. It kept failing with error codes 2 and 4 in the console. I was pretty sure that it was our proxy that was causing the grief.

I opened procmon and monitored the AUSST tool, I found that it was not even attempting to use the proxy. I checked the C:\Users\<user>\AppData\Local\Temp\AdobeUpdaterServerSetupTool.log but only found what the console was telling me. General network issues. In procmon however, I seen it was writing to a C:\Users\<user>\AppData\Local\Temp\CreativeCloud\ACC\AdobeDownload\DLM.log. The console does not tell you about it however. I opened that one up and got a little further, though not much. It did give a better error then just network issues though. This time it gave me “failed to resolve the proxy setting on the machine” along with error 12180. If you look up 12180, it largely repeats the last error, just that the proxy settings failed. Finally out of desperation I was changing the proxy settings manually and found that, if “Auto Detect” was checked in your proxy settings, then AUSST would just give up proxy, whether you have a pac file, or manually entered proxy settings or not.

I immediately ran into another issue. This one was a bit easier to troubleshoot. It was still failing to download files, but at least it actually made connections. In AdobeUpdaterServerSetupTool.log I seen “Failed to download icons”, “Failed to complete migration” and “Internal error occurred”. Now that I knew about the DLM.log, I checked that too. That was spitting out error 12175. This was again more helpful than the AdobeUpdateServerSetupTool.log’s output. If you look that error up, it roughly translates to SSL error occurred. I popped open WireShark and decided to see if I could find any handshake errors or anything else. I did not, but I did find the URL of the file that the machine was attempting to contact. I put that URL directly into Internet Explorer and immediately was met with a SSL trust error. The Root certificate for Adobe was not installed on the machine. I imported the cert and tool started working after that.

TLDR;

If you get general network type errors in AdobeUpdaterServerSetupTool.log and error 12180/”failed to resolve the proxy setting on the machine” in the DLM.log, uncheck “Auto Detect” in your proxy settings

If you get http security/12175 in DLM.log and possibly “Failed to download icons”, “Failed to complete migration” and “Internal error occurred” in AdobeUpdaterServerSetupTool.log, ensure you don’t have any issues trusting Adobe’s cert chain used on their update server.

Update: I had an additional issue with our update server. You can read it and some general client-side troubleshooting steps in this post.

Managing Adobe CC Users from PowerShell

I wanted to automate our user management of Adobe Creative Cloud. This requires interfacing with Adobe’s user management API. One of the coolest functions I created in this initiative allows you to synchronize an adobe group based on an Active Directory group. I intend to use this AD Group for AppLocker, SCCM deployments, and syncing to Adobe Creative Cloud. This should largely automate the entire Creative Cloud deployment and reduce administrative overhead. The end result will be a single administrative user adds someone to the “Approved CC Users” group, and everything else is hands free.

See the GitHub repo for the PowerShell script and additional information and resources.

Deploy Applications via SCCM 2016 PowerShell cmdlets

Continuing my PowerShell automation notes for SCCM. Below is a rough example on how to deploy Applications in SCCM 2016 using PowerShell. The real meat of it comes down to 5 cmdlets. As an extra goodie, also included the cmdlet to remove old deployments as well. Note: If you are using these cmdlets on a new machine or account, the account that is to run these cmdlets should open the SCCM console on that machine, and click the “Connect with PowerShell” option first. If the account has not performed these steps, the SCCM drive will not be available when the SCCM cmdlets are imported. You will see errors such as “A drive with the name ‘xyz’ does not exist.”

Command Run Down

The core commands we are interested in are

New-CMApplication # Creates a new application in SCCM
Add-CMScriptDeploymentType # Adds a script based deployment type or optionally
Add-​CM​Msi​Deployment​Type # Which will add an MSI deployment type

An additional note here on these two. These are currently your only deployment options and this directly limits your installation detection options. MSI is locked down to using the MSI product GUID for installation detection. If you need anything more complex than that, you are pretty much stuck with using a script based detection method and the script deployment type. The registry key and file options are not currently available. Luckily you can do nearly any detection method in PowerShell. The script below for example checks a registry key. For more information on creating a script for PowerShell based installation detection methods see the relevant docs.microsoft.com article and also David O’Brien’s blog.

Start-CMContentDistribution # Distributes our content to our Distribution Points
Start-CMApplicationDeployment # Actually deploys our finished application package to end users
Remove-CMDeployment # Removes deployments. Useful if you have an older deployment you are replacing
Move-CMObject # Moves your Application to a different folder within the SCCM console

Example Script


Param
(
    [string]$PackageDirectory="\\Path\To\Package\Source\",#Package source
    [string]$IconPath="C:\SomePath\SomeIcon.ico",#Icon to show in software center
    [string]$SCCMDrive="SCM:\",#Should be your 3 character site code generally
    [string]$SCCMAdmin="john.doe",#Owning admin
    [string]$TargetCollection="All Windows Workstations",#Collection to deploy to
    [string]$LogPath="C:\Logs\somelog.log",#Path to save log
    [string]$TargetDPs = "All Distribution Points"#Distribution point group to deploy to
)
#Start a log
$Log = "Starting Package Script`r`n"
try
{
    #Import SCCM Module
    Import-Module "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1" -ErrorAction Stop
    if (-not (Test-Path -Path $SCCMDrive))
    {
        $Log+="SCCM PowerShell cmdlets provider is not initialized for this account, on this machine. Please open the SCCM console and select 'Connect with PowerShell' at least once before using this script on thise machine.`r`n"
        throw "SCCM PSProvider does not have a drive assigned"
}
catch
{
    #End script if we could not add module
    $Log += "Failed to add required module!`r`n"
    $Log += "----End Package Script----`r`n"
    $Log | Out-File -FilePath $LogPath -Append
    exit 1
}

#TODO: Prepare files as needed here
#Maybe dynamically get file verison, or application name, unzip files if needed, etc
$Version = "1.0.0.0"
$ProductName = "Sample Application"
$ApplicatioName = "$ProductName $Version"
$Publisher = "ACME"
$InstallCommand = "`"SomeInstaller.exe`" /s"
$DetectScript = "if (Test-Path `"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\MyProdct`"){ if ((Get-ItemProperty -Path `"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\MyProduct`" -Name `"DisplayVersion`").DisplayVersion -eq `"$Version`") { Write-Host `"Installed`" } } exit 0"

try
{
    $Log += "Creating `"$ApplicationName`"`r`n"
    #Change to SCCM powershell provider, SCCM cmdlets generally do not work otherwise, however, some cmdlets may fail in the SCCM drive. Keep this in mind, you may need to switch between providers
    CD $SCCMDrive
    #Create a new application. (Won't deploy, won't distribute, won't create deployment type. Just the application info)
    $MyNewApp = New-CMApplication -Name $ApplicationName -Description "Auto-Added by Packager.ps1" -Publisher $Publisher -SoftwareVersion $Version -LocalizedName $ApplicationName `
        -Owner $SCCMAdmin -SupportContact $SCCMAdmin -IconLocationFile $IconPath -ErrorAction Stop

    #Move the application
    $Log += "Moving`"$ApplicationName`"`r`n"
    $MyNewApp | Move-CMObject -FolderPath "$($SCCMDrive)Application\SomePath\ToPlace"

    $Log += "Creating `"$ApplicationName`" - Install`r`n"
    #Add a deployment type to the new application, this won't distribute or deploy it
    Add-CMScriptDeploymentType -ApplicationName $ApplicationName -ContentLocation $PackageDirectory -ContentFallback -EnableBranchCache -InstallCommand $InstallCommand `
        -LogonRequirementType WhetherOrNotUserLoggedOn -SlowNetworkDeploymentMode Download -UserInteractionMode Hidden -InstallationBehaviorType InstallForSystem `
        -DeploymentTypeName "Install" -ScriptLanguage PowerShell -ScriptText $DetectScript -ErrorAction Stop
    #If you are doing an MSI install, look into "Add-​CM​Msi​Deployment​Type" 
    #https://docs.microsoft.com/en-us/powershell/sccm/configurationmanager/vlatest/add-cmmsideploymenttype

    $Log += "Distributing `"$ApplicationName`" - Install`r`n"
    #Distribute the content, doesnt deploy
    Start-CMContentDistribution -ApplicationName $ApplicationName -DistributionPointGroupName $TargetDPs

    $Log += "Deploying `"$ApplicationName`" - Install`r`n"
    #Deploy the new application
    Start-CMApplicationDeployment -CollectionName $TargetCollection -Name $ApplicationName -DeadlineDateTime ([DateTime]::Now) -AvailableDateTime ([DateTime]::Now) `
        -DeployAction Install -DeployPurpose Required -OverrideServiceWindow $true -TimeBaseOn LocalTime -UseMeteredNetwork $true

    $Log += "Stopping old deployments`r`n"
    #Additionally, we can stop any old deployments.
    #Grab all apps similiarly named to what we just deployed, but are not what we deployed
    $Apps = @()+(Get-CMApplication -Fast | Where-Object -FilterScript {$_.LocalizedDisplayName -like "$ProductName *" -and $_.LocalizedDisplayName -ne $ApplicationName -and $_.IsDeployed})
    foreach ($App in $Apps)
    {
        Write-Log "Deployment for $($App.LocalizedDisplayName) stopped`r`n"
        #And remove their deployment rule
        #You may need to change application name. My ApplicationName and LocalizedDisplayName usually match
        Remove-CMDeployment -CollectionName $TargetCollection -ApplicationName $App.LocalizedDisplayName -Force 
    }
}
catch
{
    Write-Log "Failed to create package. $($_.ToString())"
}

#Return to filesystem provider
cd "$($env:SystemDrive)\"

$Log += "----End Package Script----`r`n"
$Log | Out-File -FilePath $LogPath -Append
exit 0

For additional information on the cmdlets, please see the 2016 cmdlet reference at docs.microsoft.com.

Using PowerShell to watch a log

I was recently troubleshooting an issue and needed to view the last few results of a log. CMTrace and text editors would crash due to the sheer size of the log. Powershell’s Get-Content with the “Tail” parameter worked like a charm however. Although this worked, I didn’t want to keep running the command over and over, so, I decided to replicate the watch command from Linux in PowerShell.


<#
.SYNOPSIS
    Repetitively runs a script block to allow you to track changes in the command output. An example use would be for watching log inputs. Press CTRL+C to cancel script. It runs indefinitely.

.PARAMETER ScriptBlock
    Script to execute

.PARAMETER Interval
    How often to rerun scriptblock in seconds

.NOTES
    Version:        1.0
    Author:         Matthew Thompson
    Creation Date:  2017-07-19
    Purpose/Change: Initial script development
  
.EXAMPLE
    &"Start-Watch.ps1" -ScriptBlock {Get-Content -Path "C:\Logs\SomeLog.log" -Tail 20} -Interval 10
#>
Param([scriptblock]$ScriptBlock, [int32]$Interval=5)
#Put the real code in a function so it can be quickly copy-pasted as a child function of other scripts
function Start-Watch
{
    Param([scriptblock]$ScriptBlock, [int32]$Interval)
    #Set lowest possible datetime, so that it will run script immediatly
    $Start = [DateTime]::MinValue
    #Infinite loop, cancel require user intervention (CTRL+C)
    while($true)
    {
        #If enough time has passed (Now - LastAttempt)>Selected interval
        if ([DateTime]::Now - $Start -ge [TimeSpan]::FromSeconds($Interval))
        {
            #Clear console and call function
            Clear-Host
            $ScriptBlock.Invoke()
            #Set new start time/last attempt
            $Start = [DateTime]::Now
        }
        #Sleep the thread, prevents CPU from falsly registering as 100% utilized
        [System.Threading.Thread]::Sleep(1)
    }
}
#Call watch function
Start-Watch -ScriptBlock $ScriptBlock -Interval $Interval

Deploy Updates via SCCM 2016 PowerShell cmdlets

I have found very few examples on how to use the SCCM PowerShell cmdlets to deploy updates. Maybe someone will find this example useful. The following script will search the update catalog for relevant updates, add them to a Software Update Group, create a Software Update Package, download the updates contained in the Software Update Group into the Software Update Package and distribute them to a distribution point group. Then optionally, you can deploy it to a collection of machines afterwards.

Command run down

The commands specifically relating to SCCM are:

Get-CMSoftwareUpdate # Lists available updates from SCCM catalog
New-CMSoftwareUpdateGroup  # Creates a new Software Update Group
New-CMSoftwareUpdateDeploymentPackage # Creates a Software Update Package
Save-CMSoftwareUpdate # Downloads updates into a Deployment Package
Start-CMContentDistribution # Distributes downloaded content to Distribution Points

The Script


Param($SoftwareUpdateSource="\\YourShare\PathTo\SCCMSupSource", $DistributionGroup="All Distribution Points")#, $CollectionName = "All Systems") #Uncomment this if you also want to deploy to a collection as the last step 

#Used in creating update group name
$Date = [DateTime]::Now.ToString("yyyy-MM-dd");

#Grab all updates in the catalog
$UpdateCatalog = Get-CMSoftwareUpdate -Fast

#Filter out the updates we don't need
#Specifically, this filter will pull Updates created in the last 31 days that are not deployed, expired, or superseded that are not preview updates and SCCM has confirmed at least 25 machines require them
$Updates = Where-Object -FilterScript {$_.DateCreated -gt [DateTime]::Now.AddDays(-31) -and $_.IsDeployed -eq $false -and $_.LocalizedDisplayName.Contains("Preview") -eq $false -and $_.IsExpired -eq $false -and $_.IsSuperseded -eq $false -and $_.NumMissing -gt 25}

#We only need the update IDs. Newer powershell will automatically loop through each update and pull the CI_ID properties into an array with this line
$UpdateIDs = $Updates.CI_ID 

#Now we create the software update group
New-CMSoftwareUpdateGroup -Name "Security Updates $Date" -UpdateId $UpdateIDs

#Create a new folder in the Sup Source directory to contain package files
New-Item -Path "filesystem::$SoftwareUpdateSource\$Date" -ItemType Directory

#Create the deployment package
New-CMSoftwareUpdateDeploymentPackage -Name "Security Updates $Date" -Path "$SoftwareUpdateSource\$Date"

#Download the software update group to the deployment package
Save-CMSoftwareUpdate -SoftwareUpdateGroupName "Security Updates $Date" -DeploymentPackageName "Security Updates $Date" 

#And finally distribute it to your Distribution Points
Start-CMContentDistribution -DeploymentPackageName "Security Updates $Date"-DistributionPointGroupName "$DistributionGroup"

#After this you would deploy, if you want to automate that, look into the following. You will likely want/need to customize this portion.
#See "Get-Help Start-CMSoftwareUpdateDeployment" for more options 
<# Start-CMSoftwareUpdateDeployment -AcceptEula -AllowRestart $true -AllowUseMeteredNetwork $true ` -CollectionName $CollectionName -DeploymentAvailableTime ([DateTime]::Now.AddDays(1)) ` -DeploymentName "Security Updates $Date - $CollectionName" -DeploymentType Required -Description "Automatic updates" ` -DownloadFromMicrosoftUpdate $false ` -EnforcementDeadline ([DateTime]::Now.AddDays(8)) ` -ProtectedType RemoteDistributionPoint -RestartServer $false -RestartWorkstation $true -SoftwareInstallation $true ` -SoftwareUpdateGroupName "Security Updates $Date" -TimeBasedOn LocalTime -UnprotectedType NoInstall -UseBranchCache $true ` -UserNotification DisplaySoftwareCenterOnly #>

See also

Googling for SCCM PowerShell cmdlets usually returns a link to the SCCM 2012 R2 library of cmdlets on technet. I managed to find the latest SCCM 2016 cmdlet references at docs.microsoft.com.

Blog Migrated

If you were looking for the WIKI, it has now been migrated to this WordPress Page. Further all applications previously available have been made open source. Scripts and such are still available under the “Code Release” category.

Alternatively, most of the code is now available on GitHub.