Browsed by
Month: July 2015

Automated updates of Devolution’s Remote Desktop Manager using SCCM

Automated updates of Devolution’s Remote Desktop Manager using SCCM

Recently I have been spending time going back to basic’s around automation in SCCM with PowerShell to brush up on my skills. As part of this I spent a few days playing around with migrating to use Visual Studio as my development space for PowerShell with Adam Driscoll’s PowerShell Tools for Visual Studio add in, which I can’t recommend highly enough when tied to TFS online, Version becomes a super simple task of checking your code in rather than saving different versions.

So I have my Dev space all setup, and went to connect to my Lab Servers Via Devolutions Remote Desktop Manager and got this Prompt

Which annoyed me to no end as I only installed it 3 weeks ago and it appears there had already been 2 version changes. So rather than just disabling the prompt to check for updates as I would normally do on RDM, I started investigating how I can auto mate the updates of RDM without paying for a Third Party tool, and still have the latest version available from my SCCM Server.

So first things first we need to find out how the Application checks to see if there are any updated versions available, for this we can use Fiddler which is a great tool that allows to capture the Http/Https traffic leaving your computer.

Which presented us with these links:

http://remotedesktopmanager.com/products.htm

http://remotedesktopmanager.com/data/RdmChangeHistoryUpdate.htm

From here we browse to each and can see that http://remotedesktopmanager.com/products.htm returns this:

Which we can obtain the current version for each of the versions of RDM, along with where to download the application exe file from. Right about now I was thinking this is going to be a cake walk we can just step through each of the lines in the htm file and select only the ones which we need.

So we grab the web address and use the following script to obtain the htm page into a PowerShell Variable:

$wc = New-Object Net.WebClient

$srdm = $wc.DownloadString(“http://remotedesktopmanager.com/products.htm”)

And when we run $srdm in the ISE to see the list is looks like this:

This should be super easy, I’m still thinking we can use a simple foreach on $srdm like I would on a normal multi line string. I try it, and I get the all of the information back, which I must say threw me for a minute, so I tried

$srdm.count

With the following result:

I dig deeper into $srdm and find that the end of the line is a line break rather than a carriage Return as I was expecting, so after some quick research found that we can split $srdm using $srdm.Split(“`r`n”) which will separate it on the line break’s rather than the carriage returns. You can see if we now run the following command $srdm.Split(“`r`n”).count that the number is vastly larger than 1 which was the previous result

So we can now use the simple foreach to step through each line in the variable, which will look like this:

$RDMVerChk = “RDMEnterprise”

$wc = New-Object Net.WebClient

$srdm = $wc.DownloadString(“http://remotedesktopmanager.com/products.htm”)

$out = @()

foreach($rdm in $srdm.Split(“`r`n”).count)

{

$obj = New-Object PSObject

if ($rdm -like “*$RDMVerChk*”)

{

$s = $rdm.Split(“=”)

$obj | Add-Member Noteproperty Name $s[0].split(“.”)[1]

$obj | Add-Member Noteproperty Value $s[1]

$out += $obj

}

}

You will see that I have also added a check only return the strings which contain RDMEnterprise as this is the version of RDM we use, you can change the $RDMVerChk variable to the required version. The next step is to create new PSobject to capture the results into a simple Array for use later in the script.

Now the next step is to check in SCCM to check if the version available from the RDM website is currently available, and if it isn’t to publish it.

To start we need to import the Configuration Manager PowerShell module with the following command:

Import-Module $env:SMS_ADMIN_UI_PATH.Replace(“i386”,“ConfigurationManager.psd1”) -Force

Once imported we need to set the current PowerShell location to the SCCM SIteCode for this example P01

Set-Location “P01:”

Now we can completed a simple Get-CMApplication to see if the RDM Version exists, we will use the expected name of the application we create during running this script which is “Remote_Desktop_Manager-$rdmver where $rdmver is the RDM version from the Website.

$rdmver = $out | ForEach-Object {if ($_.Name -eq “Version”){$_.Value}}

if((Get-CMApplication -Name “Remote_Desktop_Manager-$rdmver))

{

write-output “Already Available”

}

else

{

write-output “Needs to be created”

}

Great now we just need to add the application to SCCM, and make it available to users via the Application Catalog. To do this we use the following script:

#download File

$wc.DownloadFile(($out | ForEach-Object {if ($_.Name -eq “exe”){$_.Value}}), $path\RDM.exe”)

#Create RDM folder and Extract MSI from exe

if(!(Test-Path -path $Path\RDM”)){New-Item -Path $Path\RDM” -Force -ItemType Directory | Out-Null}

Start-Process -FilePath $path\RDM.exe” -ArgumentList “/extract:$path\RDM” -Wait

# Extract .ico file from the RDM.exe

[System.Drawing.Icon]::ExtractAssociatedIcon($path\RDM.exe”).ToBitmap().save($path\RDM\RDM.ico”)

$path = $path\RDM”

$InstallFile = (Get-ChildItem -Path $path | where {$_.name -Like “*.msi”}).name

# Create Folders and move files around

if(!(Test-Path -path $DestPath\applications”)){New-Item -Path $DestPath\applications” -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman)){New-Item -Path $DestPath\applications\$prodman -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman\$prodname$rdmver)){New-Item -Path $DestPath\applications\$prodman\$prodname$rdmver -Force -ItemType Directory | Out-Null}

Copy-Item $path\*” $DestPath\applications\$prodman\$prodname$rdmver

# Create SCCM Application

Set-Location $Sitecode`:”

# get existing Deployment Types to be SuperSeeded

$oldappdt = Get-CMDeploymentType -ApplicationName $prodname*”

$newapp = New-CMApplication -Name $prodname$rdmver -Publisher ($prodman.replace(” “,“_”)) -SoftwareVersion $rdmver -IconLocationFile $path\rdm.ico” -LocalizedApplicationName ($prodname.replace(” “,“_”))

Add-CMDeploymentType -MsiInstaller -InstallationFileLocation $DestPath\applications\$prodman\$prodname$rdmver\$installfile -Application $newapp -ForceForUnknownPublisher:$true

# Create SuperSeedence for each existing Deployment Type

foreach ($dt in $oldappdt)

{

# You need to get the new Deployment Type each time as it changes when new SuperSeedences are added.

$appdt = Get-CMDeploymentType -ApplicationName $prodname$rdmver

Add-CMDeploymentTypeSupersedence -SupersedingDeploymentType $dt -IsUninstall $true -SupersededDeploymentType $appdt

}

# Disribution Content to a DP (this can be changed to DP Group as needed)

Start-CMContentDistribution -Application $newapp -DistributionPointName $DP

# Create New User collection for deployment, defineing the limiting collection and the collection to include (Can be changed to Query As required)

$newcol = New-CMUserCollection -Name “Install $ProdMan $Prodname $rdmver -LimitingCollectionName $LimitingCol

Add-CMUserCollectionIncludeMembershipRule -Collection $newcol -IncludeCollectionName $IncCol

# deploy New Application to New Collection

Start-CMApplicationDeployment -CollectionName ($newcol.Name) -Name ($newapp.LocalizedDisplayName)

As you can see it is pretty much broken up into 3 phases, Download & Prepare the file, Confirm if folders exist on Media Store & copy extracted files up to the new folders, And create SCCM Application, Supersede old versions & deploy the new application to a New Collection. I will focus on the first 2 phase’s as they contain the interesting parts, while the last phase is using the default create collection & deployment commands for SCCM.

Download phase, we obtain a copy of the file using this command:

$wc.DownloadFile(($out | ForEach-Object {if ($_.Name -eq “exe”){$_.Value}}), $path\RDM.exe”)

Where $wc & $out still exist from earlier use scripts

The next step is to create a scratch folder to target the exe’s /Extract command at, this will give us the following 2 files

The next step is to create the .ico we can use in the Application Catalog to make it look pretty for the end user

[System.Drawing.Icon]::ExtractAssociatedIcon($path\RDM.exe”).ToBitmap().save($path\RDM\RDM.ico”)

Which we are saving into the scratch folder to copy up to the SCCM server. We also define $InstallFile

From the MSI that exists in the scratch folder rather than using assuming that Devolutions will continue to use the same naming convention for the msi file.

The creation of the folders is pretty self-explanatory.

The next phase is to create the SCCM Applications & supersede the old versions.

We obtain the list of existing Application Deployment Types as the first step to ensure it doesn’t include the application we are creating.

With New-CMApplication in addition to the mandatory parameters we also see -LocalizedApplicationName & -IconLocationFile these are used to define the appearance of the application when published in the Application Catalog, the IconLocationFile parameter have the requirement of either a .ico or image file to work, while LocalizedApplicationName is a free text field.

With Add-CMDeploymentType we have included the following parameter -ForceForUnknownPublisher:$true which ensure that if the MSI file is not signed it will still add the deployment type, You should only use this on MSI’s you know & trust the origin of.

To Supersede the existing versions of Remote Desktop Manager we are using the Add-CMDeploymentTypeSupersedence command, as noted above, every time you add a new Superseded deployment type to the new Deployment type you will need reload the variable as it has changed.

So that’s the breakdown, the Whole script looks like this (Make sure you fix the Quotes when copying from the internet when not using PowerShell 5):

Set-Location “c:”

$Sitecode = “” #SCCM SiteCode

$path = “C:\data”

$Destpath = “” #Path to Central media store eg: \\<servername>\Source$

$dp = “” #FQDN of DP to deploy too (single DP at this point)

$LimitingCol = “” # name of limiting Collection

$IncCol = “” # name of collection to include

$RDMVerChk = “RDMEnterprise” # version of RDM

Import-Module $env:SMS_ADMIN_UI_PATH.Replace(“i386”,“ConfigurationManager.psd1”) -Force | Out-Null

$curdrv = (Get-Location).path

$wc = New-Object Net.WebClient

$srdm = $wc.DownloadString(“http://remotedesktopmanager.com/products.htm”)

$out = @()

foreach($rdm in $srdm.Split(“`r`n”))

{

$obj = New-Object PSObject

if ($rdm -like “*$RDMVerChk*”)

{

$s = $rdm.Split(“=”)

$obj | Add-Member Noteproperty Name $s[0].split(“.”)[1]

$obj | Add-Member Noteproperty Value $s[1]

$out += $obj

}

}

$rdmver = $out | ForEach-Object {if ($_.Name -eq “Version”){$_.Value}}

Set-Location $Sitecode`:”

if((Get-CMApplication -Name “Remote_Desktop_Manager-$rdmver))

{

Set-Location $curdrv

}

else

{

Set-Location $curdrv

#download File

$wc.DownloadFile(($out | ForEach-Object {if ($_.Name -eq “exe”){$_.Value}}), $path\RDM.exe”)

#Create RDM folder and Extract MSI from exe

if(!(Test-Path -path $Path\RDM”)){New-Item -Path $Path\RDM” -Force -ItemType Directory | Out-Null}

Start-Process -FilePath $path\RDM.exe” -ArgumentList “/extract:$path\RDM” -Wait

# Extract .ico file from the RDM.exe

[System.Drawing.Icon]::ExtractAssociatedIcon($path\RDM.exe”).ToBitmap().save($path\RDM\RDM.ico”)

$path = $path\RDM”

$InstallFile = (Get-ChildItem -Path $path | where {$_.name -Like “*.msi”}).name

# Create Folders and move files around

if(!(Test-Path -path $DestPath\applications”)){New-Item -Path $DestPath\applications” -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman)){New-Item -Path $DestPath\applications\$prodman -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman\$prodname$rdmver)){New-Item -Path $DestPath\applications\$prodman\$prodname$rdmver -Force -ItemType Directory | Out-Null}

Copy-Item $path\*” $DestPath\applications\$prodman\$prodname$rdmver

# Create SCCM Application

Set-Location $Sitecode`:”

# get existing Deployment Types to be SuperSeeded

$oldappdt = Get-CMDeploymentType -ApplicationName $prodname*”

$newapp = New-CMApplication -Name $prodname$rdmver -Publisher ($prodman.replace(“_”,” “)) -SoftwareVersion $rdmver -IconLocationFile $path\rdm.ico” -LocalizedApplicationName ($prodname.replace(“_”,” “))

Add-CMDeploymentType -MsiInstaller -InstallationFileLocation $DestPath\applications\$prodman\$prodname$rdmver\$installfile -Application $newapp -ForceForUnknownPublisher:$true

# Create SuperSeedence for each existing Deployment Type

foreach ($dt in $oldappdt)

{

# You need to get the new Deployment Type each time as it changes when new SuperSeedences are added.

}

# Disribution Content to a DP (this can be changed to DP Group as needed)

Start-CMContentDistribution -Application $newapp -DistributionPointName $DP

# Create New User collection for deployment, defineing the limiting collection and the collection to include (Can be changed to Query As required)

$newcol = New-CMUserCollection -Name “Install $ProdMan $Prodname $rdmver -LimitingCollectionName $LimitingCol

Add-CMUserCollectionIncludeMembershipRule -Collection $newcol -IncludeCollectionName $IncCol

# deploy New Application to New Collection

Start-CMApplicationDeployment -CollectionName ($newcol.Name) -Name ($newapp.LocalizedDisplayName)

# return to original Path

Set-Location $curdrv

}

Good Luck

Steve