Tagged: ConfigMgr

Update ConfigMgr packages with Hotfix-information

When you install a hotfix and/or a cumulative update in ConfigMgr you can select the option to let the installer create some packages.

But, those packages are missing some info… For instance Manufacturer and Version.

A quick and dirty SQL update will do the trick.

UPDATE SMSPackages_G
SET
	Version = LEFT(Replace(Source, '\\server.domain.com\SMS_ABC\hotfix\', ''), 9),
	Manufacturer = 'Microsoft',
	Language = 'All'
WHERE Source LIKE '\\server.domain.com\SMS_ABC\Hotfix%'

This is of course not supported… but afaik it works. 😀

If you want to see what would be updated, run this first

SELECT
	PkgID,
	Version AS OrgVersion,
	LEFT(Replace(Source, '\\server.domain.com\SMS_ABC\hotfix\', ''), 9) AS NewVersion,
	Manufacturer AS OrgManufacturer,
	'Microsoft' AS NewManufacturer,
	Language AS OrgLanguage,
	'All' AS NewLanguage
FROM SMSPackages_G
WHERE Source LIKE '\\server.domain.com\SMS_ABC\hotfix\%'

(And yes, you need to replace servername and sitecode)

Logon Scripts in Powershell – Part4: Trigger ConfigMgr client actions

With the ConfigMgr agent in place on your client’s you probably want to force it to start some client actions on logon to speed up deployment of new applications.

This function will work with the ConfigMgr 2007 (on x86) and 2012 agent. (The reason for not working on a Win7 x64 and ConfigMgr 2007 is the lack of X64 support).

Function Run-ConfigMgrActions {
	<#
	.SYNOPSIS 
		Trigger ConfigMgr client actions
	#>
	PARAM (
		[string] $actionFilter1 = "Application Global Evaluation Task*",
		[string] $actionFilter2 = "Request ? Evaluate*"
	)

	Write-Verbose "Run-ConfigMgrActions: Start actions with filter '$($actionFilter1)' or '$($actionFilter2)'"
	TRY {
		(New-Object -ComObject CPApplet.cpAppletMgr).GetClientActions() | Where-Object {$_.Name -like $actionFilter1 -or $_.Name -like $actionFilter2} | Sort-Object Name | ForEach-Object {
			Write-Verbose "Run-ConfigMgrActions: Starting ConfigMgr action: $($_.Name)"
			$_.PerformAction()
		}
	}
	CATCH {
		Write-Verbose "Run-ConfigMgrActions: Can't find and/or trigger ConfigMgr Agent"
	}
}

Update ConfigMgr Site Description

I did an install of a secondary site and accidently put in the wrong description…

Anyway, easy to fix with some SQL commands.

If you use this you can see the current sitenames and descriptions (change CM_XYZ to your database name):

SELECT SiteCode, SiteName, SiteServerName
FROM CM_XYZ..SC_SiteDefinition

And here is the way to update it (Change to you database name and the site code of the site you want to change)

UPDATE SC_SiteDefinition
SET SiteName = 'My Site Description'
WHERE CM_XYZ.SiteCode = 'ABC'

FYI: When running this you will se “Updated X rows… Updated Y rows…” and so on.

Set ConfigMgr 2012 Working Hours with Powershell

The “working hours” defined in ConfigMgr 2012 Software Center on the client is a bit off for our environment…

Did a bit of digging and found some examples in VBScript, but I like Powershell… 🙂

Function Set-CmClientBusinessHours {
	PARAM (
		[int] $StarTime = 7,
		[int] $EndTime = 18,
		[switch] $Sunday,
		[switch] $Monday,
		[switch] $Tuesday,
		[switch] $Wednesday,
		[switch] $Thursday,
		[switch] $Friday,
		[switch] $Saturday
	)
	
	$WorkingDays += ([int]$Sunday.IsPresent    * 1)
	$WorkingDays += ([int]$Monday.IsPresent    * 2)
	$WorkingDays += ([int]$Tuesday.IsPresent   * 4)
	$WorkingDays += ([int]$Wednesday.IsPresent * 8)
	$WorkingDays += ([int]$Thursday.IsPresent  * 16)
	$WorkingDays += ([int]$Friday.IsPresent    * 32)
	$WorkingDays += ([int]$Saturday.IsPresent  * 64)

	TRY {
		$cliUX = [WmiClass]"\\.\ROOT\CCM\ClientSDK:CCM_ClientUXSettings"
		$Params = $cliUX.PSBase.GetMethodParameters("SetBusinessHours")
		$Params.StartTime = $StarTime
		$Params.EndTime = $EndTime
		$Params.WorkingDays = $WorkingDays
		$cliUX.PSBase.InvokeMethod("SetBusinessHours", $Params, $null) | Out-Null
		Write-Verbose "Business Hours set to $($WorkingDays), between $($StarTime) - $($EndTime)"
		Return $true
	}
	CATCH {
		Write-Verbose "Can't set Business Hours"
		Return $false
	}
}

And this is how you use it (setting work days Mon-Fri between 08:00 and 17:00)

Set-CmClientBusinessHours -Monday -Tuesday -Wednesday -Thursday -Friday -StarTime 8 -EndTime 17

There are a few other methods that you can use with (almost) the same code…

# First connect to ClientUX
$cliUX = [WmiClass]"\\.\ROOT\CCM\ClientSDK:CCM_ClientUXSettings"

# Get a list of all methods
$cliUX.PSBase.methods | Format-Table Name

# Get list of parameters to a method
$cliUX.PSBase.GetMethodParameters("SetSuppressComputerActivityInPresentationMode") | Get-Member
$cliUX.PSBase.GetMethodParameters("SetAutoInstallRequiredSoftwaretoNonBusinessHours") | Get-Member
$cliUX.PSBase.GetMethodParameters("SetBusinessHours") | Get-Member

Keep a list of user/computer groups in registry

In the migration to ConfigMgr 2012 we are aiming at just keeping one collection for software distribution and instead rely on global conditions.
We do want to keep the option to add a user to a group and with that do a automagic distribution.

So, the examples below are snippets from the logonscript.

Here is the short version:
– Recursive search for group membership for user
– If group-list changed, update the reg key
(Then do the same for computer groups)
– If there are any changes to groups, trigger the ConfigMgr agent

– Create a global condition in ConfigMgr
– Use the condition like “If MyGlobalCondition contains SwDistGroupName”

$userDN = "CN=MyUser,OU=Users,DC=snowland,DC=se"
$computerDN = "CN=CyComputer,OU=Computers,DC=snowland,DC=se"

Function Get-DistGroups {
	PARAM (
		$ObjectDN,
		$nameFilter = "SwDist_*"
	)

	$distGroups = @()
	$AdSearch=([ADSISearcher]"LDAP://")
	$AdSearch.pagesize=512
	$AdSearch.Filter="(&(member:1.2.840.113556.1.4.1941:=$($ObjectDN))(name=$($nameFilter)))"
	$AdSearch.findAll() | ForEach-Object {
		Write-Verbose "Found group: $($_.Properties.Item('Name'))"
		$distGroups += $_.Properties.Item("Name")
	}

	Return $distGroups
}

# Standard setting
$runConfigMgrActions = $false

# Store DIST groups in registry
Write-Host "Reading User DIST groups"
$userDistGroups = Get-DistGroups -ObjectDN $userDN | Sort-Object
$CurrKeys = Read-RegKey -Key "HKCU:\Software\SSAB\SoftwareDistribution" -Name "UserGroups" | Sort-Object
If ((Compare-Object -ReferenceObject $CurrKeys -DifferenceObject $userDistGroups) -ne $null) {
	Write-Host "Found added/removed user groups, writing new list to registry"
	New-ItemProperty "HKCU:\Software\snowland\SoftwareDistribution" -Name "UserGroups" -Value $userDistGroups -PropertyType "MultiString" -Force | Out-Null

	$runConfigMgrActions = $true
} else {
	Write-Verbose "No changes in user groups"
}

Write-Host "Reading computer groups"
$computerDistGroups = Get-DistGroups -ObjectDN $computerDN | Sort-Object
$CurrKeys = Read-RegKey -Key "HKLM:\Software\SSAB\SoftwareDistribution" -Name "ComputerGroups" | Sort-Object

If ((Compare-Object -ReferenceObject $CurrKeys -DifferenceObject $computerDistGroups) -ne $null) {
	Write-Host "Found added/removed computer groups, writing new list to registry"
	New-ItemProperty "HKLM:\Software\snowland\SoftwareDistribution" -Name "ComputerGroups" -Value $computerDistGroups -PropertyType "MultiString" -Force | Out-Null
	$runConfigMgrActions = $true
} else {
	Write-Verbose "No changes in computer groups"
}

# Trigger ConfigMgr client actions if groups have changed
If ($runConfigMgrActions -eq $true) {
	Write-Verbose "Changes to DIST groups, running ConfigMgr Actions"
	(New-Object -ComObject CPApplet.cpAppletMgr).GetClientActions() | Where-Object {$_.Name -like "Application Global Evaluation Task*" -or $_.Name -like "Request & Evaluate*"} | Sort-Object Name | ForEach-Object {
		Write-Host "Starting ConfigMgr action: $($_.Name)"
		$_.PerformAction()
	}
}

(To do this you need to set a security GPO to the HKLM-key so that your users are allowed to write there.)

Get ConfigMgr Collection rules

I’m in the process of installing Forefront Endpoint Protection and wanted to look at some of the collection queries that was created… but with the ConfigMgr console you cant view them…

So Powershell it is.

Did a function that you can use on any collection (with subcollections) to view the WQL.

Import-Module SCCM\SCCM-Functions -Force
Function Get-CollectionRules {
	PARAM (
		$parentCollection,
		$spacer,
		$sccm
	)

	$subCollections = Get-SCCMSubCollections -SccmServer $sccm -CollectionID $parentCollection
	
	if ($subCollections -ne $null) {
		$subCollections | ForEach-Object {
			$collection = Get-SCCMCollection -Filter "CollectionID='$($_.subCollectionID)'" -SccmServer $sccm
			Write-Host "$($spacer) Name: " -ForegroundColor Yellow -NoNewline
			Write-Host "$($collection.CollectionID) - $($collection.Name)"

			$collectionRule = (Get-SCCMCollectionRules -SccmServer ( Connect-SCCMServer ) -CollectionID $collection.CollectionID)
			if ($collectionRule -ne $null) {
				Write-Host "$($spacer)Limit: " -ForegroundColor Yellow -NoNewline
				if ($collectionRule.LimitToCollectionID.Length -gt 0) {
					Write-Host "$($collectionRule.LimitToCollectionID)" -ForegroundColor White
				} else {
					Write-Host "<No limit to collection>" -ForegroundColor Gray
				}

				Write-Host "$($spacer)  WQL: " -ForegroundColor Yellow -NoNewline
				Write-Host "$($collectionRule.QueryExpression)"
			} else {
				Write-Host "$($spacer)<no rule present on collection>" -ForegroundColor Gray
			}
			Write-Host ""

			Get-CollectionRules -parentCollection $_.subCollectionID -spacer "   $($spacer)" -sccm $sccm
		}
	}
}

Get-CollectionRules -parentCollection "XYZ00123" -spacer "" -sccm (Connect-SCCMServer)

A small warning: It will loop all of the subcollections, and the subcollections subcollections, and so on…

SCCM PoSH

Changelog

  • 2013-05-09 (Ricardo Cheing)
    Bugfix: Add-SCCMDirUserCollectionRule (Corrected Get-User to Get-SCCMUser)
  • 2010-10-29 (Rikard Ronnkvist)
    New functions: Update-SCCMDriverPkgSourcePath, Update-SCCMPackageSourcePath, Update-SCCMDriverSourcePath
  • 2010-10-06 (Stefan Ringler)
    New and updated functions from http://www.stefanringler.com/?p=150 (New-SCCMPackage, New-SCCMAdvertisement, New-SCCMProgram, Add-SCCMDistributionPoint)
  • 2010-09-13 (Rikard Ronnkvist)
    Bugfix: Get-SCCMCollectionMembers (Thanks to Milos)
    Bugfix: Add-SCCMCollectionRule (Thanks to Luigi)
  • 2010-03-26 (Rikard Ronnkvist)
    Separate page on snowland.se
    New function: New-SCCMPackage
  • 2010-03-23 (Rikard Ronnkvist)
    Fixed some small bugs
    Added limitToCollectionId in Add-SCCMCollectionRule
  • 2010-03-10 (Rikard Ronnkvist)
    Major makeover
    First snowland.se release
  • 2009-04-07 (Michael Niehaus)
    Original code posted at http://blogs.technet.com/mniehaus/

Usage:

  • Save the file as SCCM-Commands.psm1
  • PS:>Import-Module SCCM-Commands
  • PS:>Get-SCCMCommands

SCCM-Commands.psm1

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#                                                                                                                            Rikard Ronnkvist / snowland.se
#  Usage:
#   Save the file as SCCM-Commands.psm1
#   PS:>Import-Module SCCM-Commands
#   PS:>Get-SCCMCommands
#
#  2009-04-07   Michael Niehaus     Original code posted at http://blogs.technet.com/mniehaus/
#  2010-03-10   Rikard Ronnkvist    Major makeover and first snowland.se release
#  2010-03-23   Rikard Ronnkvist    Fixed some small bugs and added limitToCollectionId in Add-SCCMCollectionRule
#  2010-03-26   Rikard Ronnkvist    New function: New-SCCMPackage
#  2010-09-13   Rikard Ronnkvist    Bugfixes to Add-SCCMCollectionRule and Get-SCCMCollectionMembers (Thanks to comments on snowland.se from Milos and Luigi)
#  2010-10-06   Stefan Ringler      New and updated functions from http://www.stefanringler.com/?p=150 (New-SCCMPackage, New-SCCMAdvertisement, New-SCCMProgram, Add-SCCMDistributionPoint)
#  2010-10-29   Rikard Ronnkvist    New functions: Update-SCCMDriverPkgSourcePath, Update-SCCMPackageSourcePath, Update-SCCMDriverSourcePath
#  2013-05-09   Ricardo Cheing      Bugfix: Add-SCCMDirUserCollectionRule (Corrected Get-User to Get-SCCMUser)
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Function Get-SCCMCommands {
	# List all SCCM-commands
	[CmdletBinding()]
	PARAM ()
	PROCESS {
		return Get-Command -Name *-SCCM* -CommandType Function  | Sort-Object Name | Format-Table Name, Module
	}
}

Function Connect-SCCMServer {
	# Connect to one SCCM server
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$false,HelpMessage="SCCM Server Name or FQDN",ValueFromPipeline=$true)][Alias("ServerName","FQDN","ComputerName")][String] $HostName = (Get-Content env:computername),
		[Parameter(Mandatory=$false,HelpMessage="Optional SCCM Site Code",ValueFromPipelineByPropertyName=$true )][String] $siteCode = $null,
		[Parameter(Mandatory=$false,HelpMessage="Credentials to use" )][System.Management.Automation.PSCredential] $credential = $null
	)

	PROCESS {
		# Get the pointer to the provider for the site code
		if ($siteCode -eq $null -or $siteCode -eq "") {
			Write-Verbose "Getting provider location for default site on server $HostName"
			if ($credential -eq $null) {
				$sccmProviderLocation = Get-WmiObject -query "select * from SMS_ProviderLocation where ProviderForLocalSite = true" -Namespace "root\sms" -computername $HostName -errorAction Stop
			} else {
				$sccmProviderLocation = Get-WmiObject -query "select * from SMS_ProviderLocation where ProviderForLocalSite = true" -Namespace "root\sms" -computername $HostName -credential $credential -errorAction Stop
			}
		} else {
			Write-Verbose "Getting provider location for site $siteCode on server $HostName"
			if ($credential -eq $null) {
				$sccmProviderLocation = Get-WmiObject -query "SELECT * FROM SMS_ProviderLocation where SiteCode = '$siteCode'" -Namespace "root\sms" -computername $HostName -errorAction Stop
			} else {
				$sccmProviderLocation = Get-WmiObject -query "SELECT * FROM SMS_ProviderLocation where SiteCode = '$siteCode'" -Namespace "root\sms" -computername $HostName -credential $credential -errorAction Stop
			}
		}

		# Split up the namespace path
		$parts = $sccmProviderLocation.NamespacePath -split "\\", 4
		Write-Verbose "Provider is located on $($sccmProviderLocation.Machine) in namespace $($parts[3])"

		# Create a new object with information
		$retObj = New-Object -TypeName System.Object
		$retObj | add-Member -memberType NoteProperty -name Machine -Value $HostName
		$retObj | add-Member -memberType NoteProperty -name Namespace -Value $parts[3]
		$retObj | add-Member -memberType NoteProperty -name SccmProvider -Value $sccmProviderLocation

		return $retObj
	}
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Function Get-SCCMObject {
	#  Generic query tool
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipelineByPropertyName=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="SCCM Class to query",ValueFromPipeline=$true)][Alias("Table","View")][String] $class,
		[Parameter(Mandatory=$false,HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		if ($Filter -eq $null -or $Filter -eq "")
		{
			Write-Verbose "WMI Query: SELECT * FROM $class"
			$retObj = get-wmiobject -class $class -computername $SccmServer.Machine -namespace $SccmServer.Namespace
		}
		else
		{
			Write-Verbose "WMI Query: SELECT * FROM $class WHERE $Filter"
			$retObj = get-wmiobject -query "SELECT * FROM $class WHERE $Filter" -computername $SccmServer.Machine -namespace $SccmServer.Namespace
		}

		return $retObj
	}
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Function Get-SCCMPackage {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_Package" -Filter $Filter
	}
}

Function Get-SCCMCollection {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_Collection" -Filter $Filter
	}
}

Function Get-SCCMAdvertisement {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_Advertisement" -Filter $Filter
	}
}

Function Get-SCCMDriver {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_Driver" -Filter $Filter
	}
}

Function Get-SCCMDriverPackage {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_DriverPackage" -Filter $Filter
	}
}

Function Get-SCCMTaskSequence {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_TaskSequence" -Filter $Filter
	}
}

Function Get-SCCMSite {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_Site" -Filter $Filter
	}
}

Function Get-SCCMImagePackage {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_ImagePackage" -Filter $Filter
	}
}

Function Get-SCCMOperatingSystemInstallPackage {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_OperatingSystemInstallPackage" -Filter $Filter
	}
}

Function Get-SCCMBootImagePackage {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Optional Filter on query")][String] $Filter = $null
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_BootImagePackage" -Filter $Filter
	}
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Function Get-SCCMComputer {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Filter on SCCM Resource ID",ValueFromPipelineByPropertyName=$true)][int32] $ResourceID = $false,
		[Parameter(Mandatory=$false, HelpMessage="Filter on Netbiosname on computer",ValueFromPipeline=$true)][String] $NetbiosName = "%",
		[Parameter(Mandatory=$false, HelpMessage="Filter on Domain name",ValueFromPipelineByPropertyName=$true)][Alias("Domain", "Workgroup")][String] $ResourceDomainOrWorkgroup = "%",
		[Parameter(Mandatory=$false, HelpMessage="Filter on SmbiosGuid (UUID)")][String] $SmBiosGuid = "%"
	)

	PROCESS {
		if ($ResourceID -eq $false -and $NetbiosName -eq "%" -and $ResourceDomainOrWorkgroup -eq "%" -and $SmBiosGuid -eq "%") {
			throw "Need at least one filter..."
		}

		if ($ResourceID -eq $false) {
			return Get-SCCMObject -sccmServer $SccmServer -class "SMS_R_System" -Filter "NetbiosName LIKE '$NetbiosName' AND ResourceDomainOrWorkgroup LIKE '$ResourceDomainOrWorkgroup' AND SmBiosGuid LIKE '$SmBiosGuid'"
		} else {
			return Get-SCCMObject -sccmServer $SccmServer -class "SMS_R_System" -Filter "ResourceID = $ResourceID"
		}
	}
}

Function Get-SCCMUser {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Filter on SCCM Resource ID",ValueFromPipelineByPropertyName=$true)][int32] $ResourceID = $false,
		[Parameter(Mandatory=$false, HelpMessage="Filter on unique username in form DOMAIN\UserName",ValueFromPipelineByPropertyName=$true)][String] $UniqueUserName = "%",
		[Parameter(Mandatory=$false, HelpMessage="Filter on Domain name",ValueFromPipelineByPropertyName=$true)][Alias("Domain")][String] $WindowsNTDomain = "%",
		[Parameter(Mandatory=$false, HelpMessage="Filter on UserName",ValueFromPipeline=$true)][String] $UserName = "%"
	)

	PROCESS {
		if ($ResourceID -eq $false -and $UniqueUserName -eq "%" -and $WindowsNTDomain -eq "%" -and $UserName -eq "%") {
			throw "Need at least one filter..."
		}

		if ($ResourceID -eq $false) {
			return Get-SCCMObject -sccmServer $SccmServer -class "SMS_R_User" -Filter "UniqueUserName LIKE '$UniqueUserName' AND WindowsNTDomain LIKE '$WindowsNTDomain' AND UserName LIKE '$UserName'"
		} else {
			return Get-SCCMObject -sccmServer $SccmServer -class "SMS_R_User" -Filter "ResourceID = $ResourceID"
		}
	}
}

Function Get-SCCMCollectionMembers {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="CollectionID", ValueFromPipeline=$true)][String] $CollectionID
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_CollectionMember_a" -Filter "CollectionID = '$CollectionID'"
	}
}

Function Get-SCCMSubCollections {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="CollectionID",ValueFromPipeline=$true)][Alias("parentCollectionID")][String] $CollectionID
	)

	PROCESS {
		return Get-SCCMObject -sccmServer $SccmServer -class "SMS_CollectToSubCollect" -Filter "parentCollectionID = '$CollectionID'"
	}
}

Function Get-SCCMParentCollection {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="CollectionID",ValueFromPipeline=$true)][Alias("subCollectionID")][String] $CollectionID
	)

	PROCESS {
		$parentCollection = Get-SCCMObject -sccmServer $SccmServer -class "SMS_CollectToSubCollect" -Filter "subCollectionID = '$CollectionID'"

		return Get-SCCMCollection -sccmServer $SccmServer -Filter "CollectionID = '$($parentCollection.parentCollectionID)'"
	}
}

Function Get-SCCMSiteDefinition {
	# Get all definitions for one SCCM site
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer
	)

	PROCESS {
		Write-Verbose "Refresh the site $($SccmServer.SccmProvider.SiteCode) control file"
		Invoke-WmiMethod -path SMS_SiteControlFile -name RefreshSCF -argumentList $($SccmServer.SccmProvider.SiteCode) -computername $SccmServer.Machine -namespace $SccmServer.Namespace

		Write-Verbose "Get the site definition object for this site"
		return get-wmiobject -query "SELECT * FROM SMS_SCI_SiteDefinition WHERE SiteCode = '$($SccmServer.SccmProvider.SiteCode)' AND FileType = 2" -computername $SccmServer.Machine -namespace $SccmServer.Namespace
	}
}

Function Get-SCCMSiteDefinitionProps {
	# Get definitionproperties for one SCCM site
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer
	)

	PROCESS {
		return Get-SCCMSiteDefinition -sccmServer $SccmServer | ForEach-Object { $_.Props }
	}
}

Function Get-SCCMIsR2 {
	# Return $true if the SCCM server is R2 capable
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer
	)

	PROCESS {
		$result = Get-SCCMSiteDefinitionProps -sccmServer $SccmServer | ? {$_.PropertyName -eq "IsR2CapableRTM"}
		if (-not $result) {
			return $false
		} elseif ($result.Value = 31) {
			return $true
		} else {
			return $false
		}
	}
}

Function Get-SCCMCollectionRules {
	# Get a set of all collectionrules
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="CollectionID", ValueFromPipeline=$true)][String] $CollectionID
	)

	PROCESS {
		Write-Verbose "Collecting rules for $CollectionID"
		$col = [wmi]"$($SccmServer.SccmProvider.NamespacePath):SMS_Collection.CollectionID='$($CollectionID)'"

		return $col.CollectionRules
	}
}

Function Get-SCCMInboxes {
	# Give a count of files in the SCCM-inboxes
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server",ValueFromPipeline=$true)][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$false, HelpMessage="Minimum number of files in directory")][int32] $minCount = 1
	)

	PROCESS {
		Write-Verbose "Reading \\$($SccmServer.Machine)\SMS_$($SccmServer.SccmProvider.SiteCode)\inboxes"
		return Get-ChildItem \\$($SccmServer.Machine)\SMS_$($SccmServer.SccmProvider.SiteCode)\inboxes -Recurse | Group-Object Directory | Where { $_.Count -gt $minCount } | Format-Table Count, Name -AutoSize
	}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Function New-SCCMCollection {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="Collection Name", ValueFromPipeline=$true)][String] $name,
		[Parameter(Mandatory=$false, HelpMessage="Collection comment")][String] $comment = "",
		[Parameter(Mandatory=$false, HelpMessage="Refresh Rate in Minutes")] [ValidateRange(0, 59)] [int] $refreshMinutes = 0,
		[Parameter(Mandatory=$false, HelpMessage="Refresh Rate in Hours")] [ValidateRange(0, 23)] [int] $refreshHours = 0,
		[Parameter(Mandatory=$false, HelpMessage="Refresh Rate in Days")] [ValidateRange(0, 31)] [int] $refreshDays = 0,
		[Parameter(Mandatory=$false, HelpMessage="Parent CollectionID")][String] $parentCollectionID = "COLLROOT"
	)

	PROCESS {
		# Build the parameters for creating the collection
		$arguments = @{Name = $name; Comment = $comment; OwnedByThisSite = $true}
		$newColl = Set-WmiInstance -class "SMS_Collection" -arguments $arguments -computername $SccmServer.Machine -namespace $SccmServer.Namespace

		# Hack - for some reason without this we don't get the CollectionID value
		$hack = $newColl.PSBase | select * | out-null

		# It's really hard to set the refresh schedule via Set-WmiInstance, so we'll set it later if necessary
		if ($refreshMinutes -gt 0 -or $refreshHours -gt 0 -or $refreshDays -gt 0)
		{
			Write-Verbose "Create the recur interval object"
			$intervalClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_ST_RecurInterval"
			$interval = $intervalClass.CreateInstance()
			if ($refreshMinutes -gt 0) {
				$interval.MinuteSpan = $refreshMinutes
			}
			if ($refreshHours -gt 0) {
				$interval.HourSpan = $refreshHours
			}
			if ($refreshDays -gt 0) {
				$interval.DaySpan = $refreshDays
			}

			Write-Verbose "Set the refresh schedule"
			$newColl.RefreshSchedule = $interval
			$newColl.RefreshType=2
			$path = $newColl.Put()
		}   

		Write-Verbose "Setting the new $($newColl.CollectionID) parent to $parentCollectionID"
		$subArguments  = @{SubCollectionID = $newColl.CollectionID}
		$subArguments += @{ParentCollectionID = $parentCollectionID}

		# Add the link
		$newRelation = Set-WmiInstance -Class "SMS_CollectToSubCollect" -arguments $subArguments -computername $SccmServer.Machine -namespace $SccmServer.Namespace

		Write-Verbose "Return the new collection with ID $($newColl.CollectionID)"
		return $newColl
	}
}

Function Add-SCCMCollectionRule {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true,  HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true,  HelpMessage="CollectionID", ValueFromPipelineByPropertyName=$true)] $collectionID,
		[Parameter(Mandatory=$false, HelpMessage="Computer name to add (direct)", ValueFromPipeline=$true)] [String] $name,
		[Parameter(Mandatory=$false, HelpMessage="WQL Query Expression", ValueFromPipeline=$true)] [String] $queryExpression = $null,
		[Parameter(Mandatory=$false, HelpMessage="Limit to collection (Query)", ValueFromPipeline=$false)] [String] $limitToCollectionId = $null,
		[Parameter(Mandatory=$true,  HelpMessage="Rule Name", ValueFromPipeline=$true)] [String] $queryRuleName
	)

	PROCESS {
		# Get the specified collection (to make sure we have the lazy properties)
		$coll = [wmi]"$($SccmServer.SccmProvider.NamespacePath):SMS_Collection.CollectionID='$collectionID'"

		# Build the new rule
		if ($queryExpression.Length -gt 0) {
			# Create a query rule
			$ruleClass = [WMICLASS]"$($SccmServer.SccmProvider.NamespacePath):SMS_CollectionRuleQuery"
			$newRule = $ruleClass.CreateInstance()
			$newRule.RuleName = $queryRuleName
			$newRule.QueryExpression = $queryExpression
			if ($limitToCollectionId -ne $null) {
				$newRule.LimitToCollectionID = $limitToCollectionId
			}

			$null = $coll.AddMembershipRule($newRule)
		} else {
			$ruleClass = [WMICLASS]"$($SccmServer.SccmProvider.NamespacePath):SMS_CollectionRuleDirect"

			# Find each computer
			$computer = Get-SCCMComputer -sccmServer $SccmServer -NetbiosName $name
			# See if the computer is already a member
			$found = $false
			if ($coll.CollectionRules -ne $null) {
				foreach ($member in $coll.CollectionRules) {
					if ($member.ResourceID -eq $computer.ResourceID) {
						$found = $true
					}
				}
			}
			if (-not $found) {
				Write-Verbose "Adding new rule for computer $name"
				$newRule = $ruleClass.CreateInstance()
				$newRule.RuleName = $name
				$newRule.ResourceClassName = "SMS_R_System"
				$newRule.ResourceID = $computer.ResourceID

				$null = $coll.AddMembershipRule($newRule)
			} else {
				Write-Verbose "Computer $name is already in the collection"
			}
		}
	}
}

Function Add-SCCMDirUserCollectionRule {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(ValueFromPipelineByPropertyName=$true)][String] $CollectionID,
		[Parameter(ValueFromPipeline=$true)][String] $UserName
	)

	PROCESS {
		$coll = [wmi]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_Collection.CollectionID='$CollectionID'"
		$ruleClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_CollectionRuleDirect"

		$RuleClass
		$UserRule=Get-SCCMUser "userName='$UserName'"
		$NewRuleName=$UserRule.name
		$NewRuleResourceID = $UserRule.ResourceID
		$newRule = $ruleClass.CreateInstance()

		$newRule.RuleName = $NewRuleName
		$newRule.ResourceClassName = "SMS_R_User"
		$newRule.ResourceID = $NewRuleResourceID

		$null = $coll.AddMembershipRule($newRule)
		$coll.requestrefresh()
		Clear-Variable -name oldrule -errorAction SilentlyContinue
		Clear-Variable -name Coll -errorAction SilentlyContinue
	}
}

Function New-SCCMPackage {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="Package Name", ValueFromPipeline=$true)][String] $Name,

		[Parameter(Mandatory=$false, HelpMessage="Package Version")][String] $Version = "",
		[Parameter(Mandatory=$false, HelpMessage="Package Manufacturer")][String] $Manufacturer = "",
		[Parameter(Mandatory=$false, HelpMessage="Package Language")][String] $Language = "",
		[Parameter(Mandatory=$false, HelpMessage="Package Description")][String] $Description = "",
		[Parameter(Mandatory=$false, HelpMessage="Package Data Source Path")][String] $PkgSourcePath = "",
		[Parameter(Mandatory=$false, HelpMessage="Package Sharename")][String] $PkgShareName = ""
	)

	PROCESS {
		$packageClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_Package"
		$newPackage = $packageClass.createInstance() 

		$newPackage.Name = $Name
		if ($Version -ne "") 		{ $newPackage.Version = $Version }
		if ($Manufacturer -ne "") 	{ $newPackage.Manufacturer = $Manufacturer }
		if ($Language -ne "") 		{ $newPackage.Language = $Language }
		if ($Description -ne "") 	{ $newPackage.Description = $Description }

		if ($PkgSourcePath -ne "") {
			$newPackage.PkgSourceFlag = 2  # Direct (3 = Compressed)
			$newPackage.PkgSourcePath = $PkgSourcePath
			if ($PkgShareName -ne "") {
				$newPackage.ShareName = $PkgShareName
				$newPackage.ShareType = 2
			}
		} else {
			$newPackage.PkgSourceFlag = 1  # No source
			$newPackage.PkgSourcePath = $null
		}
		$newPackage.Put()

		$newPackage.Get()
		Write-Verbose "Return the new package with ID $($newPackage.PackageID)"
		return $newPackage
	}
}

Function New-SCCMAdvertisement {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true)] $AdvertisementName,
		[Parameter(Mandatory=$true)] $collectionID,
		[Parameter(Mandatory=$true)] $PackageID,
		[Parameter(Mandatory=$true)] $ProgramName,
		[Switch] $Download,
		[Parameter(Mandatory=$false, HelpMessage="YYYYMMDDhhmm")] $StartTime,
		[Parameter(Mandatory=$false, HelpMessage="YYYYMMDDhhmm")] $EndTime,
		[Parameter(Mandatory=$false, HelpMessage="YYYYMMDDhhmm")] $MandatoryTime
	)
	PROCESS {
		$strServer = $SccmServer.machine
		$strNamespace= $SccmServer.namespace
		$AdvClass = [WmiClass]("\\$strServer\" + "$strNameSpace" + ":SMS_Advertisement")
		if ($Download) {
			$RemoteClientFlags = "3152"
		} else {
			$RemoteClientFlags = "3208"
		}
		if ($StartTime -ne $null) {
			$PresentTime = $StartTime + "00.000000+***"
		} else {
			$PresentTime = "20200110000000.000000+***"
		}
		if ($EndTime -ne $null) {
			$ExpirationTime = $Endtime + "00.000000+***"
			$ExpirationTimeEnabled = $true
		} else {
			$ExpirationTime = "20200113000000.000000+***"
			$ExpirationTimeEnabled = $false
		}
		if ($MandatoryTime -ne $null) {
			$Deadline = $MandatoryTime + "00.000000+***"
		} else {
			$Deadline = $null
		}

		# Get the all the Advertisement Properties
		$newAdvertisement = $AdvClass.CreateInstance()
		$newAdvertisement.AdvertisementName = $AdvertisementName
		$newAdvertisement.CollectionID = $collectionID
		$newAdvertisement.PackageID = $PackageID
		$newAdvertisement.ProgramName = $ProgramName
		$newAdvertisement.RemoteClientFlags = $RemoteClientFlags
		$newAdvertisement.PresentTime = $PresentTime
		$newAdvertisement.ExpirationTime = $ExpirationTime
		$newAdvertisement.ExpirationTimeEnabled = $ExpirationTimeEnabled
		$newAdvertisement.Priority = "2"
		$newAdvertisement.IncludeSubCollection = $false

		# Create Advertisement
		$retval = $newAdvertisement.psbase.Put()
		if ($Deadline -ne $null) {
			# Create Mandatory Schedule
			$wmiClassSchedule = [WmiClass]("\\$strServer\" + "$strNameSpace" + ":SMS_ST_NonRecurring")
			$AssignedSchedule = $wmiClassSchedule.psbase.createinstance()
			$AssignedSchedule.starttime = $Deadline
			if ($Download) {
				$newAdvertisement.RemoteClientFlags = "9296"
			} else {
				$newAdvertisement.RemoteClientFlags = "9352"
			}
			$newAdvertisement.AssignedSchedule = $AssignedSchedule
			$newAdvertisement.AssignedScheduleEnabled = $true
			$newAdvertisement.psbase.put()
			$NewAdvertisementProperties = $newAdvertisement.AssignedSchedule
			foreach ($Adv in $NewAdvertisementProperties) {
				write-verbose "Created Advertisement. Name = $($newAdvertisement.AdvertisementName)"
				write-verbose "Created Advertisement. ID = $newAdvertisement"
				Write-Verbose "Mandatory Deadline created: $($Adv.StartTime)"
			}
		} else {
			write-verbose "Created Advertisement. Name = $($newAdvertisement.AdvertisementName)"
			write-verbose "Created Advertisement. ID = $newAdvertisement"
			write-verbose "No Mandatory-Deadline defined"
		}
	}
}

Function New-SCCMProgram {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="Program Name")][String] $PrgName = "",
		[Parameter(Mandatory=$true, HelpMessage="Program PackageID")]$PrgPackageID,
		[Parameter(Mandatory=$false, HelpMessage="Program Comment")][String] $PrgComment = "",
		[Parameter(Mandatory=$false, HelpMessage="Program CommandLine")][String] $PrgCommandLine = "",
		[Parameter(Mandatory=$false, HelpMessage="Program MaxRunTime")]$PrgMaxRunTime,
		[Parameter(Mandatory=$false, HelpMessage="Program Diskspace Requirement")]$PrgSpaceReq,
		[Parameter(Mandatory=$false, HelpMessage="Program Working Directory")][String] $PrgWorkDir = "",
		[Parameter(Mandatory=$false, HelpMessage="Program Flags")] $PrgFlags
	)
	PROCESS {
		$programClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_Program"
		$newProgram = $programClass.createInstance()
		$newProgram.ProgramName = $PrgName
		$newProgram.PackageID = $PrgPackageID
		if ($PrgComment -ne "") { $newProgram.Comment = $PrgComment }
		if ($PrgCommandLine -ne "") { $newProgram.CommandLine = $PrgCommandLine }
		if ($PrgMaxRunTime -ne $null) { $newProgram.Duration = $PrgMaxRunTime} else { $newProgram.Duration = "0" }
		if ($PrgSpaceReq -ne $null) { $newProgram.DiskSpaceReq = $PrgSpaceReq }
		if ($PrgWorkDir -ne "") { $newProgram.WorkingDirectory = $PrgWorkDir }
		if ($PrgFlags -ne $null) { $newProgram.ProgramFlags = $PrgFlags} else { $newProgram.ProgramFlags = "135290880" }
		$newProgram.Put()
		$newProgram.Get()
		Write-Verbose "Return the new program for Package $($newProgram.PackageID)"
		return $newProgram
	}
}

Function Add-SCCMDistributionPoint {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="PackageID")][String] $DPPackageID,
		[Parameter(Mandatory=$false, HelpMessage="DistributionPoint Servername")][String]$DPName = "",
		[Parameter(Mandatory=$false, HelpMessage="All DistributionPoints of SiteCode")][String] $DPsSiteCode = "",
		[Parameter(Mandatory=$false, HelpMessage="Distribution Point Group")][String] $DPGroupName = "",
		[Switch] $AllDPs
	)
	PROCESS {
		if ($DPName -ne "") {
			$Resource = Get-SCCMObject -SccmServer $SccmServer -class SMS_SystemResourceList -Filter "RoleName = 'SMS Distribution Point' and Servername = '$DPName'"
			$DPClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_DistributionPoint"
			$newDistributionPoint = $DPClass.createInstance()
			$newDistributionPoint.PackageID = $DPPackageID
			$newDistributionPoint.ServerNALPath = $Resource.NALPath
			$newDistributionPoint.SiteCode = $Resource.SiteCode
			$newDistributionPoint.Put()
			$newDistributionPoint.Get()
			Write-Verbose "Assigned Package: $($newDistributionPoint.PackageID)"
		}
		if ($DPsSiteCode -ne "") {
			$ListOfResources = Get-SCCMObject -SccmServer $SccmServer -class SMS_SystemResourceList -Filter "RoleName = 'SMS Distribution Point' and SiteCode = '$DPsSiteCode'"
			$DPClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_DistributionPoint"
			$newDistributionPoint = $DPClass.createInstance()
			$newDistributionPoint.PackageID = $DPPackageID
			foreach ($resource in $ListOfResources) {
				$newDistributionPoint.ServerNALPath = $Resource.NALPath
				$newDistributionPoint.SiteCode = $Resource.SiteCode
				$newDistributionPoint.Put()
				$newDistributionPoint.Get()
				Write-Verbose "Assigned Package: $($newDistributionPoint.PackageID)"
			}
		}
		if ($DPGroupName -ne "") {
			$DPGroup = Get-SCCMObject -sccmserver $SccmServer -class SMS_DistributionPointGroup -Filter "sGroupName = '$DPGroupName'"
			$DPGroupNALPaths = $DPGroup.arrNALPath
			$DPClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_DistributionPoint"
			$newDistributionPoint = $DPClass.createInstance()
			$newDistributionPoint.PackageID = $DPPackageID
			foreach ($DPGroupNALPath in $DPGroupNALPaths) {
				$DPResource = Get-SCCMObject -SccmServer $SccmServer -class SMS_SystemResourceList -Filter "RoleName = 'SMS Distribution Point'" | Where-Object {$_.NALPath -eq $DPGroupNALPath}
				if ($DPResource -ne $null) {
					Write-Verbose "$DPResource"
					$newDistributionPoint.ServerNALPath = $DPResource.NALPath
					Write-Verbose "ServerNALPath = $($newDistributionPoint.ServerNALPath)"
					$newDistributionPoint.SiteCode = $DPResource.SiteCode
					Write-Verbose "SiteCode = $($newDistributionPoint.SiteCode)"
					$newDistributionPoint.Put()
					$newDistributionPoint.Get()
					Write-Host "Assigned Package: $($newDistributionPoint.PackageID) to $($DPResource.ServerName)"
				} else {
					Write-Host "DP not found = $DPGroupNALPath"
				}
			}
		}
		if ($AllDPs) {
			$ListOfResources = Get-SCCMObject -SccmServer $SccmServer -class SMS_SystemResourceList -Filter "RoleName = 'SMS Distribution Point'"
			$DPClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_DistributionPoint"
			$newDistributionPoint = $DPClass.createInstance()
			$newDistributionPoint.PackageID = $DPPackageID
			foreach ($resource in $ListOfResources) {
				$newDistributionPoint.ServerNALPath = $Resource.NALPath
				$newDistributionPoint.SiteCode = $Resource.SiteCode
				$newDistributionPoint.Put()
				$newDistributionPoint.Get()
				Write-Verbose "Assigned Package: $($newDistributionPoint.PackageID) $($newDistributionPoint.ServerNALPath)"
			}
		}
	}
}

Function Update-SCCMDriverPkgSourcePath {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="Current Path", ValueFromPipeline=$true)][String] $currentPath,
		[Parameter(Mandatory=$true, HelpMessage="New Path", ValueFromPipeline=$true)][String] $newPath
	)

	PROCESS {
		Get-SCCMDriverPackage -sccmserver $SccmServer | Where-Object {$_.PkgSourcePath -ilike "*$($currentPath)*" } | Foreach-Object {
			$newSourcePath = ($_.PkgSourcePath -ireplace [regex]::Escape($currentPath), $newPath)
			Write-Verbose "Changing from '$($_.PkgSourcePath)' to '$($newSourcePath)' on $($_.PackageID)"
			$_.PkgSourcePath = $newSourcePath
			$_.Put() | Out-Null
		}
	}
}

Function Update-SCCMPackageSourcePath {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="Current Path", ValueFromPipeline=$true)][String] $currentPath,
		[Parameter(Mandatory=$true, HelpMessage="New Path", ValueFromPipeline=$true)][String] $newPath
	)

	PROCESS {
		Get-SCCMPackage -sccmserver $SccmServer | Where-Object {$_.PkgSourcePath -ilike "*$($currentPath)*" } | Foreach-Object {
			$newSourcePath = ($_.PkgSourcePath -ireplace [regex]::Escape($currentPath), $newPath)
			Write-Verbose "Changing from '$($_.PkgSourcePath)' to '$($newSourcePath)' on $($_.PackageID)"
			$_.PkgSourcePath = $newSourcePath
			$_.Put() | Out-Null
		}
	}
}

Function Update-SCCMDriverSourcePathnotepad {
	[CmdletBinding()]
	PARAM (
		[Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer,
		[Parameter(Mandatory=$true, HelpMessage="Current Path", ValueFromPipeline=$true)][String] $currentPath,
		[Parameter(Mandatory=$true, HelpMessage="New Path", ValueFromPipeline=$true)][String] $newPath
	)

	PROCESS {
		Get-SCCMDriver -sccmserver $SccmServer | Where-Object {$_.ContentSourcePath -ilike "*$($currentPath)*" } | Foreach-Object {
			$newSourcePath = ($_.ContentSourcePath -ireplace [regex]::Escape($currentPath), $newPath)
			Write-Verbose "Changing from '$($_.ContentSourcePath)' to '$($newSourcePath)' on $($_.PackageID)"
			$_.ContentSourcePath = $newSourcePath
			$_.Put() | Out-Null
		}
	}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# EOF

Examples

# List all available SCCM commands
Get-SCCMCommands

# Create an SCCM-Connection to the local server
$sccm = Connect-SCCMServer -Verbose

# Create a new collection with a querybased collection rule and a direct member
$newCollection = New-SCCMCollection -SccmServer $sccm -name "Some Collection Name" -Verbose
Add-SCCMCollectionRule -Server $sccm -collectionID $newRoot.CollectionId -queryExpression "SELECT * FROM SMS_R_System" -queryRuleName "All Systems" -Verbose
Add-SCCMCollectionRule -Server $sccm -collectionID $newRoot.CollectionId -name "COMPUTER123" -Verbose

# Count files in the inboxes
$sccm | Get-SCCMInboxes

# Get a package
$MyPackage = Get-SCCMPackage -server $sccm -filter "Name = 'Some Package Name'"