Skip to content

Commit

Permalink
Added lots of awesomeness
Browse files Browse the repository at this point in the history
  • Loading branch information
adbertram committed Aug 30, 2015
1 parent 81ec6f1 commit 1782133
Show file tree
Hide file tree
Showing 62 changed files with 7,470 additions and 0 deletions.
50 changes: 50 additions & 0 deletions Connect-CmRemoteTools.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<#
.SYNOPSIS
This script calls the SCCM console's remote tools clients (CmRcViewer.exe) to start a SCCM
remote tools console from Powershell.
.NOTES
Created on: 12/9/2014
Created by: Adam Bertram
Filename: Connect-CmRemoteTools.ps1
Requirements: An available SCCM 2012 site server and the SCCM console installed
Permissions to connect to the remote computer
.EXAMPLE
PS> .\Connect-CmRemoteTools.ps1 -Computername MYCOMPUTER
This example would bring up the SCCM remote tools console window connecting to the computer called MYCOMPUTER
.PARAMETER Computername
The name of the computer you'd like to use remote tools to connect to
.PARAMETER
The name of the SCCM site server holding the site database
#>
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateScript({Test-Connection -ComputerName $_ -Quiet -Count 1})]
[string]$Computername,
[ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1 })]
[string]$SiteServer = 'CONFIGMANAGER'
)

begin {
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
Set-StrictMode -Version Latest
}

process {
try {
## Find the path of the admin console to get the path of the remote tools client
if (!$env:SMS_ADMIN_UI_PATH -or !(Test-Path "$($env:SMS_ADMIN_UI_PATH)\CmRcViewer.exe")) {
throw "Unable to find the SCCM remote tools exe. Is the console installed?"
} else {
$RemoteToolsFilePath = "$($env:SMS_ADMIN_UI_PATH)\CmRcViewer.exe"
}

& $RemoteToolsFilePath $Computername "\\$SiteServer"

} catch {
Write-Error $_.Exception.Message
}
}
303 changes: 303 additions & 0 deletions Convert-CMApplicationToPackage.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
#Requires -Version 3
#Requires -Module ConfigurationManager

<#
.SYNOPSIS
This creates a ConfigMgr package/program for each deployment type from the attributes of a ConfigMgr application.
.DESCRIPTION
This reads a ConfigMgr application and gathers all needed attributes from the application itself and it's
deployment types. It then creates a package/program for each deployment type in the application. If more than one
deployment type exists the resulting package(s) will be named $ApplicationName - $DeploymentTypeName" with each
program being named $DeploymentTypeName.
.NOTES
Created on: 07/03/2014
Created by: Adam Bertram
Filename: Convert-CMApplicationToPackage.ps1
Credits: http://www.david-obrien.net/2014/01/24/convert-configmgr-applications-packages-powershell/
Todos: If the app has a product code, use this for installation program management in the program
Create an option to distribute the package to DPs after creation
.DESCRIPTION
This gets all common attributes of a ConfigMgr application that a ConfigMgr package/program has and uses
these attributes to create a new packages with a program inside for each application deployment type.
.EXAMPLE
.\Convert-CMApplicationToPackage.ps1 -ApplicationName 'Application 1'
This example converts the application "Application 1" into a package called "Application 1" and a program
called "Install Application 1" if it has a single deployment type.
.EXAMPLE
.\Convert-CMApplicationToPackage.ps1 -ApplicationName 'Application 1' -SkipRequirements
This example converts the application "Application 1" into a package called "Application 1" and a program
called Install "Application 1" excluding disk space and OS requirements if it has a single deployment type.
.PARAMETER ApplicationName
This is the name of the application you'd like to convert.
.PARAMETER PackageName
This is the name of the package you'd like to create. If this param isn't used and only 1 deployment type
exists in the application, it will default to the name of the application else if the application has multiple
deployment types it will default to the application name and the name of the deployment type.
.PARAMETER SkipRequirement
Use this switch parameter if you don't want to bring over any disk or OS requirements from the application.
.PARAMETER DistributeContent
Use this swtich to find all DPs/DP Groups the application is distributed to and distribute the package to them after
the package has been created.
.PARAMETER OsdFriendlyPowershellSyntax
Use this switch parameter to convert any program that's simply a reference to a PS1 file that normally works
in a non-OSD environment to a full powershell syntax using powershell.exe.
.PARAMETER AdditionalOptions
An array of hashtables of any additional options that will be applied to the resulting package. Use the form
@(@{'Package' = @{ 'Property' = 'Value' } }) or @(@{'Program' = @{ 'Property' = 'Value' } })
.PARAMETER SiteServerName
The ConfigMgr site server name
.PARAMETER SiteCode
The ConfigMgr site code.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string]$ApplicationName,
[string]$PackageName,
[switch]$SkipRequirements,
[switch]$DistributeContent,
[switch]$OsdFriendlyPowershellSyntax,
[array]$AdditionalOptions = @(@{'Package' = @{ 'PkgFlags' = '128' } }),
[string]$SiteServer = 'CONFIGMANAGER',
[string]$SiteCode = 'UHP'
)

begin {
try {
## This helper function gets all of the supported platform objects that's supported for creating OS requirements for a package,
## looks for a match between each CI_UniqueID and the OS string and if there's a match, creates a new lazy property instance
## populates the necessary values and returns an array of objects that can be used to populated the SupportOperatingSystemPlatforms
## lazy WMI property on the SMS_Program object.
function New-SupportedOsObject([Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.Rule]$OsRequirement) {
$SupportedPlatforms = Get-WmiObject -ComputerName $SiteServer -Class SMS_SupportedPlatforms -Namespace "root\sms\site_$SiteCode"
$SupportedOs = @()
## Define the array of OS strings to convert to objects
if ($OsRequirement.Expression.Operator.OperatorName -eq 'OneOf') {
$AppOsList = $OsRequirement.Expression.Operands.RuleId
} elseif ($OsRequirement.Expression.Operator.OperatorName -eq 'NoneOf') {
## TODO: Query the site server for all possible operating system values and remove all OSes in
## $OsRequirement.DeploymentTypes[0].Requirements[0].Expression.Operands.RuleId
return $false
}
foreach ($AppOs in $AppOsList) {
foreach ($OsDetail in $SupportedPlatforms) {
if ($AppOs -eq $OsDetail.CI_UniqueId) {
$instance = ([wmiclass]("\\$SiteServer\root\sms\site_$SiteCode`:SMS_OS_Details")).CreateInstance()
if ($instance -is [System.Management.ManagementBaseObject]) {
$instance.MaxVersion = $OsDetail.OSMaxVersion
$instance.MinVersion = $OsDetail.OSMinVersion
$instance.Name = $OsDetail.OSName
$instance.Platform = $OsDetail.OSPlatform
$SupportedOs += $instance
}
}
}
}
$SupportedOs
}

function Convert-NalPathToName ($NalPath) {
$NalPath.Split('\\')[2].Split('.')[0]
}

function Get-DpsinDpGroup ($GroupId) {
$Dps = Get-WmiObject @SiteServerWmiProps -Class SMS_DPGroupMembers -Filter "GroupID = '$GroupId'"
if ($Dps) {
$Dps.DPNALPath | foreach { Convert-NalPathToName $_ }
} else {
$false
}
}

if (!(Test-Path "$(Split-Path $env:SMS_ADMIN_UI_PATH -Parent)\ConfigurationManager.psd1")) {
throw 'Configuration Manager module not found. Is the admin console intalled?'
} elseif (!(Get-Module 'ConfigurationManager')) {
Import-Module "$(Split-Path $env:SMS_ADMIN_UI_PATH -Parent)\ConfigurationManager.psd1"
}
$Location = (Get-Location).Path
Set-Location "$($SiteCode):"

$Application = Get-CMApplication -Name $ApplicationName
if (!$Application) {
throw "$ApplicationName not found"
}
$ApplicationXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($Application.SDMPackageXML)

$SetProgramProps = @{ }

$SiteServerWmiProps = @{
'Computername' = $SiteServer;
'Namespace' = "root\sms\site_$SiteCode"
}

} catch {
Write-Error $_.Exception.Message
exit
}
}
process {
try {
$DeploymentTypes = $ApplicationXML.DeploymentTypes

for ($i = 0; $i -lt $DeploymentTypes.Count; $i++) {
if ($DeploymentTypes.Count -gt 1) {
$PackageName = "$ApplicationName - $($ApplicationXML.DeploymentTypes[$i].Title)"
} elseif (!$PackageName) {
$PackageName = $ApplicationName
}
$ProgramName = $ApplicationXML.DeploymentTypes[$i].Title

if (Get-CMPackage -Name $PackageName) {
throw "$PackageName already exists"
}

$PackageProps = @{
'Name' = $PackageName;
'Version' = $ApplicationXML.SoftwareVersion;
'Manufacturer' = $ApplicationXML.Publisher;
'Path' = $ApplicationXML.DeploymentTypes[$i].Installer.Contents.Location;
}

## 07/03/2014 - Even though the New-CMProgram documentation leads you to believe you can use a string for the RunType
## param, it won't work. You must use the [Microsoft.ConfigurationManagement.Cmdlets.AppModel.Commands.RunType] object.
$NewProgramProps = @{
'StandardProgramName' = $ProgramName;
'PackageName' = $PackageName;
'RunType' = [Microsoft.ConfigurationManagement.Cmdlets.AppModel.Commands.RunType]::($ApplicationXML.DeploymentTypes[$i].Installer.UserInteractionMode)
}

$AppCmdLine = $ApplicationXML.DeploymentTypes[$i].Installer.InstallCommandLine
## If the command line is simply a reference to a single PS1 file
if ($OsdFriendlyPowershellSyntax.IsPresent -and ($AppCmdLine -match '.ps1$')) {
$NewProgramProps.CommandLine = "powershell.exe -ExecutionPolicy bypass -NoProfile -NoLogo -NonInteractive -File $AppCmdLine"
} else {
$NewProgramProps.CommandLine = $ApplicationXML.DeploymentTypes[$i].Installer.InstallCommandLine
}

$SetProgramProps = @{
'EnableTaskSequence' = $true;
'StandardProgramName' = $ProgramName;
'Name' = $PackageName;
}

## 07/03/2014 - Due to a bug in the New-CMprogram cmdlet, even though 15 min or 720 min is allowed via the GUI
## for the max run time, it doesn't work via the New-CMProgram cmdlet. To compensate, I'm adding or removing
## 1 minute and this works.
$Duration = $ApplicationXML.DeploymentTypes[$i].Installer.MaxExecuteTime
if ($Duration -eq 15) {
$Duration = $Duration + 1
} elseif ($Duration -eq 720) {
$Duration = $Duration - 1
}
$NewProgramProps.Duration = $Duration

if (!$SkipRequirements.IsPresent) {
$Requirements = $ApplicationXML.DeploymentTypes[$i].Requirements
$RequirementExpressions = $Requirements.Expression
$FreeSpaceRequirement = $RequirementExpressions | where { ($_.Operands.LogicalName -contains 'FreeDiskSpace') -and ($_.Operator.OperatorName -eq 'GreaterEquals') }
if ($FreeSpaceRequirement) {
$NewProgramProps.DiskSpaceRequirement = $FreeSpaceRequirement.Operands.value / 1MB
$NewProgramProps.DiskSpaceUnit = 'MB'
}
}

switch ($ApplicationXML.DeploymentTypes[$i].Installer.RequiresLogon) {
$false {
$NewProgramProps.ProgramRunType = 'OnlyWhenNoUserIsLoggedOn'
}
$true {
$NewProgramProps.ProgramRunType = 'OnlyWhenUserIsLoggedOn'
}
default {
$NewProgramProps.ProgramRunType = 'WhetherOrNotUserIsLoggedOn'
}
}

if ($ApplicationXML.DeploymentTypes[$i].Installer.UserInteractionMode -eq 'Hidden') {
$SetProgramProps['SuppressProgramNotifications'] = $true
}

if ($ApplicationXML.DeploymentTypes[$i].Installer.SourceUpdateCode) {
##TODO: Look into setting installation source management on the package
}

$PostIntallBehavior = $ApplicationXML.DeploymentTypes[$i].Installer.PostInstallBehavior
if (($PostIntallBehavior -eq 'BasedOnExitCode') -or ($PostIntallBehavior -eq 'NoAction')) {
$SetProgramProps.AfterRunningType = 'NoActionRequired'
} elseif ($PostIntallBehavior -eq 'ProgramReboot') {
$SetProgramProps.AfterRunningType = 'ProgramControlsRestart'
} elseif ($PostIntallBehavior -eq 'ForceReboot') {
$SetProgramProps.AfterRunningType = 'ConfigurationManagerRestartsComputer'
}

$NewPackage = New-CMPackage @PackageProps
Write-Verbose "Successfully created package name $($NewPackage.Name) ($($NewPackage.PackageID))"
$NewProgram = New-CMProgram @NewProgramProps
Set-CMProgram @SetProgramProps

if (!$SkipRequirements.IsPresent) {
$OsRequirement = $Requirements | where { $_.Expression -is [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.OperatingSystemExpression] }
if ($OsRequirement) {
$SupportedOs = New-SupportedOsObject $OsRequirement
$NewProgram.SupportedOperatingSystems = $SupportedOs
$NewProgram.Put()
}
}

if ($AdditionalOptions) {
$AdditionalOptions | foreach {
$_.GetEnumerator() | foreach {
if ($_.Key -eq 'Package') {
$_.Value.GetEnumerator() | foreach {
$NewPackage.($_.Key) = $_.Value
$NewPackage.Put()
}
} elseif ($_.Key -eq 'Program') {
$_.Value.GetEnumerator() | foreach {
$NewProgram.($_.Key) = $_.Value
$NewProgram.Put()
}
}
}
}
}

if ($DistributeContent.IsPresent) {
## Distribute the converted package to all DP groups the application is a part of
$AllDpGroupPackages = Get-WmiObject @SiteServerWmiProps -Class SMS_DPGroupPackages
$AllDpGroups = Get-WmiObject @SiteServerWmiProps -Class SMS_DistributionPointGroup

## TODO: This currently doesn't support applications in multiple groups
$AppDpGroupId = ($AllDpGroupPackages | where { $_.PkgID -eq $Application.PackageID } | Group-Object GroupId).Name
$AppDpGroup = $AllDpGroups | where { $_.GroupID -eq $AppDpGroupId}
if ($AppDpGroup) {
Write-Verbose "Application is in a DP group"
Start-CMContentDistribution -DistributionPointGroupName $AppDpGroup.Name -PackageName $PackageName
$DpsInAppDpGroup = Get-DpsinDpGroup $AppDpGroupId
$SingleDps = Get-WmiObject @SiteServerWmiProps -Class SMS_DistributionPoint -Filter "SecureObjectID = '$($Application.ModelName)'" | where { $DpsInAppDpGroup -notcontains (Convert-NalPathToName $_.ServerNALPath) }
} else {
$SingleDps = Get-WmiObject @SiteServerWmiProps -Class SMS_DistributionPoint -Filter "SecureObjectID = '$($Application.ModelName)'"
}

if ($SingleDps) {
Write-Verbose "Application is in $($SingleDps.Count) single DPs"
foreach ($Dp in $SingleDps) {
$DpName = Convert-DpNalPathToName $Dp.ServerNALPath
Write-Verbose "Adding package '$PackageName' to DP '$DpName'"
Start-CMContentDistribution -DistributionPoint $DpName -PackageName $PackageName
}
}
}
Unlock-CMObject $NewPackage
}

} catch {
Write-Error "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
}
}

end {
Set-Location $Location
}
Loading

0 comments on commit 1782133

Please sign in to comment.