Automating the Application Readiness Process with PowerShell

bkelly
Moderator Moderator
Moderator
2 5 2,796

AdminStudio Enterprise Edition includes a robust set of PowerShell cmdlets that allow for extensive control of the solution which enables powerful automation possibilities that can significantly speed up the process of application packaging and deployment. Saving time and money is great, but of course a solid process also helps with quality– all of which you can realize with the help of the resources outlined in this article.

The Application Readiness process includes the basic areas of effort required for a successful application deployment. It often varies from company to company, but a complete process that mitigates the most risk typically includes the tasks pictured below. The presentation and script found here is intended to help you automate the bulk of the activities necessary in execution of this process.

app-ready-process.png

The AdminStudio documentation includes a reference to the cmdlets as well as a PowerShell example, but until recently we did not have a good real-world implementation of how to leverage this valuable feature to automate the Application Readiness Process.

Below you’ll find two pieces of content to help you unlock the potential of AdminStudio’s PowerShell support: A video of a presentation delivered at the 2018 App Management Event that details a script which can automate the process of importing an application, running tests, automatically fixing detected issues, converting it for virtualization and even publishing to a systems management solution for distribution. The presentation starts by explaining the process, covering some of the key cmdlets that make it possible, and finally a demonstration of the script itself. The script discussed is the second valuable piece of content shared below. To avoid some of the security implications that come with posting a PowerShell script file for download, the script can be found below for copy/paste into your editor of choice (just be sure to paste it as plain text to avoid the HTML that might otherwise come along with it depending on your editor).

 

(Full credit for the script below goes to Flexera Technical Product Manager, Kiran Mantagi who presented such in the video above)

 

###############################################################
# Environment Settings/Values
###############################################################
$DefaultExt = #<list all the package types by extension which monitored for> Eg: @(‘*.msi’,’*.appv’,’*.exe’)
$folder = #<Path to a folder which should be monitored for new incoming packages> Eg:”C:\Users\admin_kiran\Desktop\Sample Packages”
$global:CatalogName = #<Name of the application catalog> Eg:’MyCatalog’
$ConnectionString = #<Connection string to the SQL Server> Eg: PROVIDER=SQLOLEDB.1;Data Source=<sql server here>; Initial Catalog=’ + $global:CatalogName + ‘;user ID=<Username>;password=<password>’
$SCCMServer = #<SCCM Server> Eg:’10.10.10.10(or FQDN)’
$SCCMServerSiteCode = #<SCCM Site Code> Eg:’ABC’
$SCCMRepositoryPath = #<Content location where the packages from AdminStudio will be copied to distribute to SCCM. It should be a UNC Path> Eg: ‘\\10.10.10.10\Content’
$SCCMTargetGroup = #<Target Group in SCCM where the application should be published> Eg: ‘Applications\Sales’
$SCCMUser = #<SCCM Username. Essentially with admin privileges> Eg: ‘testdomain\admin’
$SCCMPass = #<SCCM User Password> Eg:’Hello’
$sAACProjectFile = #<Path to the aacx project file> Eg:”C:\AppVConversions\Convert.aacx”
$VirtualApps = #<Path to the folder where the virtualized apps to be copied> Eg: “C:\Virtualized Apps\”
$DistributionName = #<The name of the Distribution Connection like you want it to be set in AdminStudio> Eg: ‘ConfigMgr 2016′
$PluginID = 13
$ShareUser = #<The user account which has access to $SCCMRepositoryPath> Eg:’testdomain\admin’
$SharePwd = #<Password for the user account: $ShareUser> Eg:’Hello’
$IntervalinMins = #<Time in minutes to monitor the $folder for new packages copied in last these many minutes> Eg: 5 (Packages copied in last 5 mins will be picked up for processing)
$logfile = #<Log file appended with the time stamp> Eg: “C:\Logs\log $(get-date -f yyyy-MM-dd-hh-mm).txt”

###############################################################
# System Settings/Values
###############################################################
$shive = HKLM:\SOFTWARE\Wow6432Node\InstallShield\AdminStudio\16.0\
$slocation = Product Location
$sAsLoc = (Get-ItemProperty $shive $slocation).$slocation
$sCurrentLoc = [Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath
$sAsLoc = $sAsLoc + Common\
$global:oPkgArray = @()
$global:oPkgArrayError = @()
$global:oPkgArrayPass = @()
$global:oPkgArrayFail = @()
$global:oPkgVirtPass = @()
$global:oAppVPkgArrayError = @()
$global:oAppVPkgArray = @()

###############################################################
# Functions
###############################################################

###****************************** Importing single MSI package ******************************####

function Import ($s)
{
$f = [System.IO.File]::GetAttributes($s)
$d = ($f -band [System.IO.FileAttributes]::Directory)
if (!$d)
{

Write-Host Importing:’ $s -foregroundcolor white
Out-File -FilePath $logfile -Append -InputObject Importing: $s -Encoding string
$obj = Invoke-ASImportPackage -PackagePath $s
if ($obj.GetType().FullName -eq AdminStudio.Platform.Helpers.PackageHelper)
{
$global:oPkgArray = $global:oPkgArray + $obj
}
else
{
Write-Host Failed to import:’ $s -foregroundcolor red
Out-File -FilePath $logfile -Append -InputObject Failed to import: $s -Encoding string
$global:oPkgArrayError = $global:oPkgArrayError + $obj
}
}
}

###****************************** Importing MSI packages from a folder ******************************####

function ImportFolder ()
{

if ($folder)
{
Write-Host Importing Applications from $folder -foregroundcolor yellow
Out-File -FilePath $logfile -Append -InputObject Importing Applications from $folder -Encoding string
Write-Host
Out-File -FilePath $logfile -Append -InputObject  -Encoding string
foreach ($file in Get-Childitem -include $DefaultExt -Recurse $folder){

$createtime = $file.CreationTime
$nowtime = get-date
if (($nowtime  $createtime).totalminutes -lt 5)

{

Import ($file)
}

}
Write-Host
Out-File -FilePath $logfile -Append -InputObject  -Encoding string
Write-Host Packages that Imported Successfully:’ $global:oPkgArray.Count
$importedcount = $global:oPkgArray.Count
Out-File -FilePath $logfile -Append -InputObject Packages that Imported Successfully: $importedcount -Encoding string
Write-Host Packages that Failed to Import:’ $global:oPkgArrayError.Count
$failedcount = $global:oPkgArrayError.Count
Out-File -FilePath $logfile -Append -InputObject Packages that Failed to Import: $failedcount  -Encoding string
Write-Host
Out-File -FilePath $logfile -Append -InputObject  -Encoding string
}
}

###****************************** Initial Configurations ******************************####

function LoadDLL ($s)
{
$FileName = $sAsLoc + $s
import-module -name $FileName
}

function PrepAS ()
{

Set-ExecutionPolicy Unrestricted -Force
LoadDLL AdminStudio.Platform.PowerShellExtensions.dll
LoadDLL AdminStudio.Utilities.dll
LoadDLL AdminStudio.SCCM.Model.dll
LoadDLL AdminStudio.SCCM.Integrator.dll
Set-ASConfigPlatform -ConnectionString $ConnectionString

}

###****************************** Run Tests on a Package ******************************####

function Test ($o)
{
Write-Host Testing Package:’ $o.DisplayedProductName -nonewline -foregroundcolor white
Out-File -FilePath $logfile -Append -InputObject Testing Package: $($o.DisplayedProductName) -Encoding string
Write-Host  RowId:’ $o.RowID -foregroundcolor gray
$oTestResults = Test-ASPackage -PackageId $o.RowID
$errors = 0;
$warn = 0;
$fixable=0;
foreach ($oTestResult in $oTestResults.Stats)
{
$errors = $errors + $oTestResult.Errors
$warn = $warn + $oTestResult.Warnings
$fixable = $fixable + $oTestResult.Fixable

}
Write-Host-Indent
Write-Host Errors:’ $errors -foregroundcolor red
Out-File -FilePath $logfile -Append -InputObject Errors: $($errors) -Encoding string
Write-Host-Indent
Write-Host Warnings:’ $warn -foregroundcolor yellow
Out-File -FilePath $logfile -Append -InputObject Warnings: $($warn) -Encoding string
Write-Host-Indent
Write-Host Fixables:’ $fixable -foregroundcolor Magenta
Out-File -FilePath $logfile -Append -InputObject Fixables: $($fixable) -Encoding string
Write-Host

}

###****************************** Resolve fixable issues for a package**************************###

function Resolve ($o)
{
Write-Host Resolving Package:’ $o.DisplayedProductName -nonewline -foregroundcolor white
Out-File -FilePath $logfile -Append -InputObject Resolving Package: $($o.DisplayedProductName) -Encoding string
Write-Host  RowId:’ $o.RowID -foregroundcolor gray
$oTestResults = Resolve-ASPackage -PackageId $o.RowID
$errors = 0;
$warn = 0;
$fixable=0;
foreach ($oTestResult in $oTestResults.Stats)
{
$errors = $errors + $oTestResult.Errors
$warn = $warn + $oTestResult.Warnings
$fixable = $fixable + $oTestResult.Fixable

}
Write-Host-Indent
Write-Host Errors After Resolve:’ $errors -foregroundcolor red
Out-File -FilePath $logfile -Append -InputObject Errors After Resolve: $($errors) -Encoding string
Write-Host-Indent
Write-Host Warnings After Resolve:’ $warn -foregroundcolor yellow
Out-File -FilePath $logfile -Append -InputObject Warnings After Resolve: $($warn) -Encoding string
Write-Host-Indent
Write-Host Fixables After Resolve:’ $fixable -foregroundcolor Magenta
Out-File -FilePath $logfile -Append -InputObject Fixables After Resolve: $($fixable) -Encoding string
Write-Host

if ($errors -eq 0)
{

$global:oPkgArrayPass = $global:oPkgArrayPass + $o

}
else
{
$global:oPkgArrayFail = $global:oPkgArrayFail + $obj
}
}

###****************************** Resolve fixable issues for all the imported packages ******************************####

function ResolveImportedPackages ($Array)
{
$global:oPkgArrayPass = @()
$global:oPkgArrayFail = @()
foreach ($oPkg in $Array)
{
Resolve ($oPkg);
}
}

###****************************** Run Tests on all the imported packages ******************************####

function TestImportedPackages ($Array)
{
$global:oPkgArrayPass = @()
$global:oPkgArrayFail = @()
foreach ($oPkg in $Array)
{
Test ($oPkg);
}
}

###****************************** Distribute Packages to SCCM ******************************####

function DistributePackage($Array)
{

New-ASDistributionConnection -Name $DistributionName -PluginID $PluginID -ServerAddress $SCCMServer -SiteCode $SCCMServerSiteCode -DistributionWindowsAuthentication 0 -DistributionUser $SCCMUser -DistributionPassword $SCCMPass -SharePath $SCCMRepositoryPath -ShareWindowsAuthentication 0 -ShareUserName $ShareUser -SharePassword $SharePwd

foreach ($oPkg in $Array)
{

Write-Host Distributing the Package:’ $oPkg.DisplayedProductName -nonewline -foregroundcolor white
Out-File -FilePath $logfile -Append -InputObject Distributing the Package: $($oPkg.DisplayedProductName) RowId: $($oPkg.RowID) -Encoding string
Write-Host  RowId:’ $oPkg.RowID -foregroundcolor gray
$oAppID = Get-ASApplicationID -PackageID $oPkg.RowID
Invoke-ASPublish -ConnectionName $DistributionName -ApplicationID $oAppID -TargetGroup $SCCMTargetGroup
}
}

###****************************** Check Virtualization Readiness and Virtualize the MSI Packages ******************************####

function VirtualizeApp($Array)
{

foreach ($Package in $Array)
{

$VirtResult = Get-ASVirtualReadiness -PackagePath $Package.FileName -PackageId $Package.RowID

if ($VirtResult -eq 0)
{

Write-Host Converting Package:’ $Package.DisplayedProductName -foregroundcolor white
Out-File -FilePath $logfile -Append -InputObject Converting Package: $($Package.DisplayedProductName) -Encoding string
$virt=Invoke-ASConvertPackageEx -PackageId $Package.RowID -AACSettings $sAACProjectFile -TargetType AppV5
$global:oPkgVirtPass = $global:oPkgVirtPass + $Package

}
else
{

Write-Host Skipping Conversion of $Package.DisplayedProductName due to Errors/Warnings-foregroundcolor red
Out-File -FilePath $logfile -Append -InputObject Skipping Conversion of $($Package.DisplayedProductName) due to Errors/Warnings -Encoding string

}

}

}

function Write-Host-Indent ()
{
Write-Host   -nonewline

}

function Write-Host-Drawline ()
{
Write-Host ************************************** -foregroundcolor yellow
Out-File -FilePath $logfile -Append -InputObject **************************************
}

################################################################
# Main Loop
###############################################################
cd $sAsLoc
Write-Host
Out-File -FilePath $logfile -Append -InputObject  -Encoding string
Write-Host-Drawline
Write-Host Default Directory = $folder -foregroundcolor gray
Out-File -FilePath $logfile -Append -InputObject Default Directory = $folder -Encoding string
Write-Host Catalog Name= $global:CatalogName -foregroundcolor gray
Out-File -FilePath $logfile -Append -InputObject Catalog Name= $global:CatalogName -Encoding string
Write-Host-Drawline
Write-Host
Out-File -FilePath $logfile -Append -InputObject 

PrepAS
New-ASCatalog -CatalogName $global:CatalogName
ImportFolder
TestImportedPackages ($global:oPkgArray)
ResolveImportedPackages ($global:oPkgArray)
VirtualizeApp ($global:oPkgArray)
DistributePackage ($global:oPkgVirtPass)
DistributePackage ($global:oPkgArrayPass)

Write-Host
Out-File -FilePath $logfile -Append -InputObject 
Write-Host Thank you :-)
Out-File -FilePath $logfile -Append -InputObject Thank you :-)
Write-Host
Out-File -FilePath $logfile -Append -InputObject 

Send-MailMessage -To <To email addresses list> -From <From email address> -Subject AdminStudio Automation Test Results -SmtpServer smtp.acresso.com -Attachments $logfile

cd $sCurrentLoc
Tags (1)
5 Comments
madhupn
Level 3

@bkelly 

All examples show importing an application using the PSCmdlets and then performing additional steps on the application using the PackageID.

While checking the Cmdlets the ItemID/PackageID needs to be supplied for many of them to work. For e.g.: Get-ASCatalogItem

The problem is, i can't find these IDs through the Application Manager, nor i can get this information by using a Cmdlet, along with an Application Group name or package name, to query the Catalog.

So how are we supposed to retrieve those ID for applications that are already in the Catalog, in case we want automate actions performed on those applications/packages?

bkelly
Moderator Moderator
Moderator

@madhupn 

You have indeed identified a problem we have today.  We are working to provide a cmdlet which will return details of all the applications in the catalog along with their Package ID.

You can see the Package ID in the user interface but that does not help with automation. It's not elegant, but a current workaround would be to query the database where you'll find Package ID in the ‘cstblPackage’ table as RowID:

package-id.jpg

 

madhupn
Level 3

@bkelly thanks much for getting back.

Yes, i saw that option to figure out the ID from the table cstblPackage for Package IDs and cstblGroups for Groups and Application IDs, as in your screenshot or query from sql. But these are not ideal and cumbersome for automation. It would be really great to have a cmdlet to query application based on application name, group name or package name. It would be beneficial to get the group path where the application/package is located as well along with the ID and other information about application. In my company we have a group structure to import specific type of application to specific groups.  

 

Also, once i import an application, is there a cmdlet that lets me rename the Application Group for the newly imported application? It is possible to rename the Application Group and Package under it through the UI. Also I can rename the Package using the Set-ASProperty cmdlet, but this doesn't allow renaming of the Application Group. If we perform bulk imports we need to rename the Application to match with the naming standard we follow and cannot leave it at the default name created.  

bkelly
Moderator Moderator
Moderator

The workaround to get the Package ID is just that-- a workaround. We understand the need and are planning to offer a cmdlet to make this easier. 

There is no cmdlet to rename an application group. The Set-ASProperty cmdlet is to set the appmodel properties. I can take this as an enhancement request. 

Thank you for this valuable feedback @madhupn !

madhupn
Level 3
Thank you so much @ bkelly.

It would be great to have those so we can better automate the application
management through AdminStudio

Really appreciate your time and response.

--
Madhu P. Nair
Director, Product Management Charlotte, NC
Latest Articles