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 "" -ForegroundColor Gray
                                }

                                Write-Host "$($spacer)  WQL: " -ForegroundColor Yellow -NoNewline
                                Write-Host "$($collectionRule.QueryExpression)"
                        } else {
                                Write-Host "$($spacer)" -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…


Win7 Themes and Screensaver

If you want to enforce users to use a specific screensaver you can do most of it via standard group policies. But if you run Windows 7 and a user changes the current theme… the screensaver will be blank until the next group policy refresh.
This is due to that the default .theme-files have no screensaver defined.

With Group Policy Preferences you can change this…

First we need to change the current ACL on the themes directories since SYSTEM cant write there.

Edit or create a Group Policy.
Browse to Computer Configuration – Windows Settings – Security Settings – File System
Right Click and select Add File… then write %SystemRoot%\Resources\Themes in the Folder-box.
Set the security rights as you want them, but remember to give SYSTEM the rights to Modify.
In the dialog “Add Object” that pops up when you press OK, select Replace existing permissions on all suboflders and files with inheritable permissions, this option is not selected as a default.
Repeat that for %SystemRoot%\Resources\Ease of Access Themes directory.

Then browse to Computer Configuration – Preferences – Windows Settings – Ini Files
Right Click and select New – Ini File
Give the following options:

(I would recommend that you set a item level targeting to check that the file exist)

Now to the boring part, repeat that for all Theme-files in the directories %SystemRoot%\Resources\Themes and %SystemRoot%\Resources\Ease of Access Themes

If there is something else you want to change you can find loads of options for themes in this reference http://msdn.microsoft.com/en-us/library/bb773190%28v=vs.85%29.aspx


Bulk import of SNMP devices to OpsMgr

If you want to import a larger bunch of SNMP-devices in to OpsMgr you will probably go thru the disovery wizard way to many times.

Instead of walking thru that wizard every time I asked the network team to write a CSV-file with all the devices and then used this function to import them.

Function Add-SnmpDevice {
        PARAM (
                [Parameter(Mandatory=$true )][string] $FromIpAddress,
                [Parameter(Mandatory=$true )][string] $MonitoringProxy,
                [string] $ManagementServer,
                [string] $ToIpAddress = "",
                [string] $SnmpCommunityString = "public",
                [int32] $SnmpVersion = 2
        )

        # Single ip ?
        If ($ToIpAddress.Length -eq 0) {
                $ToIpAddress = $FromIpAddress
        }

        # Check SNMP version
        if ($SnmpVersion -ne 1 -and $SnmpVersion -ne 2) {
                Throw "Only SNMP version 1 and 2 supported"
        }

        Write-Host "Setting up discovery for SNMP-devices..."
        Write-Host "       From: $($FromIpAddress)"
        Write-Host "         To: $($ToIpAddress)"
        Write-Host "  Community: $($SnmpCommunityString)"
        Write-Host "   SNMP ver: $($SnmpVersion)"

        $networkDeviceClass = Get-MonitoringClass -name "System.NetworkDevice"
        $DeviceDiscoveryConfig = New-DeviceDiscoveryConfiguration -MonitoringClass $networkDeviceClass -FromIpAddress $FromIpAddress -ToIpAddress $ToIpAddress

        # Set Community String
        $encoding = New-Object System.Text.UnicodeEncoding
        $encodedCommunityString = $encoding.GetBytes($SnmpCommunityString)
        $DeviceDiscoveryConfig.ReadOnlyCommunity = [System.Convert]::ToBase64String($encodedCommunityString)

        # Set SNMP version
        $DeviceDiscoveryConfig.SnmpVersion = $SnmpVersion

        # Get management server
        If ($ManagementServer.Length -eq 0) {
                $mgmtServer = Get-RootManagementServer
        } else {
                $mgmtServer = Get-ManagementServer | Where-Object {$_.Name -eq $ManagementServer}
        }
        If ($mgmtServer -eq $null) {
                Throw "Cant find management server named $($ManagementServer)"
        } else {
                Write-Host "Found management server: $($mgmtServer.name)"
        }

        # Find proxy agent
        Write-Host "Lookup of proxy agent named $($MonitoringProxy) ..."
        $ProxyAgent = Get-Agent | Where-Object {$_.Name -eq $MonitoringProxy}
        If ($ProxyAgent -eq $null) {
                Write-Host "No agent named $($MonitoringProxy) found, checking managementservers"
                $ProxyAgent = Get-ManagementServer | Where-Object {$_.Name -eq $MonitoringProxy}
                $ProxyIsMgmtServer = $true
        } else {
                $ProxyIsMgmtServer = $false
        }

        If ($ProxyAgent -eq $null) {
                Throw "Can't find agent or managementserver named $($MonitoringProxy)"
        } else {
                Write-Host "Found $($ProxyAgent.Name)"
        }

        Write-Host "Starting discovery..."
        $DiscResults = Start-Discovery -ManagementServer: $mgmtServer -DeviceDiscoveryConfiguration: $DeviceDiscoveryConfig

        If ($DiscResults.CustomMonitoringObjects.Count -eq 0) {
                Write-Host "Cant discover any objects"
                Return 0
        } else {
                $ObjectCount = 0
                Write-Host "Found objects"
                $discresults | select-object -expandproperty CustomMonitoringObjects | Select-Object Name | Format-Table -HideTableHeaders

                $DiscResults | ForEach-Object {
                        Write-Host "Adding object to proxy..."
                        if ($ProxyIsMgmtServer -eq $true) {
                                $ProxyAgent.InsertRemotelyManagedDevices($_.CustomMonitoringObjects) | Format-Table SnmpDevice, ProxyAgentPrincipalName, ManagementGroup -HideTableHeaders
                        } else {
                                Add-RemotelyManagedDevice -proxyagent $ProxyAgent -device $_.CustomMonitoringObjects
                        }
                        $ObjectCount++
                }
                Return $ObjectCount
        }
}

And two examples on how you can use it to add some devices:

# Add a devices in the range 192.168.100.240-.254 with the community "SomeSecret"
# Use mgmtserver.snowland.demo to do the discovery and add the devices with snmpmonitor.snowland.demo as monitoring proxy
Add-SnmpDevice -FromIpAddress "192.168.100.240" -ToIpAddress "192.168.100.254" -SnmpCommunityString "SomeSecret" -ManagementServer "mgmtserver.snowland.demo" -MonitoringProxy "snmpmonitor.snowland.demo"

# Add a single SNMPv1 device with the "public" community, use the RMS to do discovery
Add-SnmpDevice -FromIpAddress "192.168.100.10" -MonitoringProxy "snmpmonitor.snowland.demo" -SnmpVersion 1

So we ended up with something like this:

Import-Csv ".\snmplist.csv" -Delimiter ";" | ForEach-Object {
        Add-SnmpDevice -FromIpAddress $_.IpFrom -ToIpAddress $_.IpTo -SnmpCommunityString $_.Community -ManagementServer $_.MgmtServer -MonitoringProxy $_.Proxy
}

InstallShield – No Log

Ok… It’s kind of hard to find any information on how to completely remove the need for a logfile when installing a InstallShield based setup file.

You can use:
-f2[some\path\to\a\LogFile]
To say where you want the logfile to go.

But… If you completely want to silence it use:
-f2x

It’s that simple, but it isn’t that simple to find the information.


Change Source-paths in ConfigMgr

I’m in the process of moving tons of packages to a new source.

So… I did a few new functions to my Powershell Module https://snowland.se/sccm-posh/ :-)

Update-SCCMDriverPkgSourcePath -sccmserver (Connect-SCCMServer) -currentPath "\\OLDSERVER\Source\DriverPackages" -newPath "\\NEWSERVER\Source\DriverPackages"
Update-SCCMPackageSourcePath -sccmserver (Connect-SCCMServer) -currentPath "\\OLDSERVER\Source\Packages" -newPath "\\NEWSERVER\Source\Packages"
Update-SCCMDriverSourcePath -sccmserver (Connect-SCCMServer) -currentPath "\\OLDSERVER\Source\DriverImport" -newPath "\\NEWSERVER\Source\DriverImport"

Oh… and I found some additional updates posted in German by Stefan Ringler … I don’t understand that many words of German, but I can read Powershell. :-P

Anyway, I included the updates in the module… thanks Stefan for sharing.


Create GPOs with Powershell

We are in the process of migrating to a brand spankin new Active Directory … and since it’s new there are no GPOs yet.

To automate and keep a strict naming convention we will use a self service portal to create GPOs.
This portal will have a few dropdown-boxes with options to minimize the risk of an user not creating the GPO as we want…

Anyway. This portal will fire a Powershell script that actualy creates the GPO and sets a bunch of things on it.

This script will:

  • Creates an AD-group
  • Creates an GPO
  • Remove Authenticated Users from GPO Security Filtering
  • Add a Administrator-group to the GPO
  • Adds a group with editing access to the GPO
  • Add the AD-Group created in the first step to Security Filtering on GPO
  • Disable Policy Computer/User Settings depending on the GPO scope
  • Add GPO-link to a Computer- or User-OU

Actually our script will give a few other groups and services (Advanced Group Policy Management – AGPM – to give one example) access to the GPOs and we create a Test-GPO as well… but I guess this is a good start for many of you.

PARAM (
        [string] $gpoScope = "U",
        [string] $gpoDescription = "PowershellTesting01",
        [string] $groupPrefix = "MyPrefix_L_",

        [string] $groupPath = "OU=All Groups,DC=snowland,DC=se",
        [string] $gpoLinkPathC = "OU=All Computers,DC=snowland,DC=se",
        [string] $gpoLinkPathU = "OU=All Users,DC=snowland,DC=se",

        [string] $gpoAdminsitrators = "MyPrefix_L_Role-GPO-Administrators",
        [string] $gpoEditors = "MyPrefix_L_Role-GPO-Editors"
)

Import-Module GroupPolicy
Import-Module ActiveDirectory

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
$gpoName = "GPO-$($gpoScope)-$($gpoDescription)"
$adGroupName = "$($groupPrefix)$($gpoName)"
$domainName = (Get-ADDomain).NetBIOSName
$dcServer = (Get-ADDomaincontroller).HostName

Write-Host "Settings:" -ForegroundColor Cyan
Write-Host "   AD GroupName       : $($adGroupName)" -ForegroundColor Cyan
Write-Host "   GPO Name           : $($gpoName)" -ForegroundColor Cyan
Write-Host "   GPO Prod           : $($gpoNameProd)" -ForegroundColor Cyan
Write-Host "   GPO Scope          : $($gpoScope)" -ForegroundColor Cyan
Write-Host "   Domain Controller  : $($dcServer)" -ForegroundColor Cyan
Write-Host "   Domain Name        : $($domainName)" -ForegroundColor Cyan
Write-Host "" -ForegroundColor Cyan

Write-Host "AD: Create AD group -" -ForegroundColor Cyan
New-ADGroup -Name $adGroupName -Description "GPO $($gpoScope) $($gpoDescription)" -GroupScope DomainLocal -Path $groupPath -Server $dcServer

Write-Host "Policy: Create policy" -ForegroundColor Cyan
New-GPO -Name $gpoName -Comment "$($gpoScope) $($gpoDescription)" -Server $dcServer

Write-Host "10 second pause to give AD a chanse to catch up" -ForegroundColor Cyan
Start-Sleep -Seconds 10

Write-Host "Remove Authenticated Users from GPO Security Filtering" -ForegroundColor Cyan
Set-GPPermissions -Name $gpoName -PermissionLevel None -TargetName "Authenticated Users" -TargetType Group -Server $dcServer

Write-Host "Add Administrators to GPO" -ForegroundColor Cyan
Set-GPPermissions -Name $gpoName -PermissionLevel GpoEditDeleteModifySecurity -TargetName $gpoAdminsitrators -TargetType group -Server $dcServer

Write-Host "Add Editors to GPO" -ForegroundColor Cyan
Set-GPPermissions -Name $gpoName -PermissionLevel GpoEdit -TargetName $gpoEditors -TargetType group -Server $dcServer

Write-Host "Add AD-Group to Security Filtering on GPO" -ForegroundColor Cyan
Set-GPPermissions -Name $gpoName -PermissionLevel GpoApply -TargetName "$($adGroupName)" -TargetType Group -Server $dcServer

If ($gpoScope -eq "C") {
        Write-Host "Disable Policy User Settings" -ForegroundColor Cyan
        (Get-GPO -Name $gpoName -Server $dcServer).GpoStatus = "UserSettingsDisabled"

        Write-Host "Add GPO-link to Computer OU" -ForegroundColor Cyan
        New-GPLink -Name $gpoName -Target $gpoLinkPathC -LinkEnabled Yes -Server $dcServer
} else {
        Write-Host "Disable Policy Computer Settings" -ForegroundColor Cyan
        (Get-GPO -Name $gpoName -Server $dcServer).GpoStatus = "ComputerSettingsDisabled"

        Write-Host "Add GPO-link to User OU" -ForegroundColor Cyan
        New-GPLink -Name $gpoName -Target $gpoLinkPathU -LinkEnabled Yes -Server $dcServer
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Write-Host "" -ForegroundColor Cyan
Write-Host "Done!" -ForegroundColor Cyan

Now I only need to figure out how to get AGPM to take control of the GPO …


Maintenance Mode via Powershell Remoting

There are loads of scripts and GUIs that you can use to set Maintenance Mode in OpsMgr, but if you want to do this from a server that doesn’t have the OpsMgr-snapins for Powershell it’s a bit harder…

But then there is Powershell v2 and Remoting… It gives you the option to run a scriptblock on another computer…

Just enable remoting on your RMS and then try this script from another machine:

Function setMaintMode {
        PARAM (
                [string] $rmsHostname,
                [string] $agentName,
                [string] $Comment,
                [string] $Reason,
                [int] $Time
        )

        Invoke-Command -ComputerName $rmsHostname -scriptblock {
                PARAM (
                        [string] $agentName,
                        [string] $Comment,
                        [string] $Reason,
                        [int] $Time
                )

                Add-PSSnapin "Microsoft.EnterpriseManagement.OperationsManager.Client"
                Set-Location "OperationsManagerMonitoring::"
                New-ManagementGroupConnection -ConnectionString:localhost | Out-Null

                $computerClass = Get-MonitoringClass -name:Microsoft.Windows.Computer
                $computerCriteria = "PrincipalName='" + $agentName + "'"
                $computer = get-monitoringobject -monitoringclass:$computerClass -criteria:$computerCriteria

                if ($computer.InMaintenanceMode -eq $false) {
                        $startTime = [System.DateTime]::Now
                        $endTime = $startTime.AddMinutes($Time)

                        New-MaintenanceWindow -startTime $startTime -endTime $endTime -Comment $comment -Reason $Reason -monitoringObject $computer
                        return $true
                }  else {
                        # Allready in maintenance mode
                        return $false
                }

        } -ArgumentList $agentName, $Comment, $Reason, $Time
}

setMaintMode -rmsHostname "rmsserver.domain.local" -agentName "currentserver.domain.local" -Comment "Some comment" -Time 30 -Reason "PlannedOperatingSystemReconfiguration"

What it does is that it run’s the OpsMgr-specific parts on the RMS instead on your local machine… so with that in place it’s easy to create a GUI around it and then spread a shortcut to all your servers that have Powershell v2 installed.

Notes:
The quick and dirty way to enable remoting on your rms, start cmd as an administrator and run winrm quickconfig
Here can you find a quick intro to PS Remoting.


ConfigMrg Native Mode and site signing certificate

After trying to switch to native mode in ConfigMgr we got some errors from SMS_POLICY_PROVIDER saying “SMS Policy Provider has failed to sign one or more policy assignments. It will retry this operation automatically.”

Strange since we did follow a (this one) step-by-step guide from Microsoft.

After a few searches on Google and TechNet I found out that I needed to add a few lines in the request-file… FriendlyName and KeyLength…

[NewRequest]
FriendlyName = "ConfigMgr Site Signing ABC"
Subject = "CN=The site code of this site server is ABC"
MachineKeySet = True
KeyLength = 2048

[RequestAttributes]
CertificateTemplate = ConfigMgrSiteServerSigningCertificate

Then I requested a new cert with that file and used the new certificate instead… and a few minutes later SMS_POLICY_PROVIDER says “SMS Policy Provider successfully updated a settings policy and a settings policy assignment.”

:-)


Change a folder LastWriteTime based on files within it

A few days ago I wrote a script that copies some files. Did notice that everything except the date on the folders were ok. So I added a few more lines of powershell code.

Did find a few suggestions on the web, but I like this one…. Since I wrote it. :-P

Function Set-FolderDate {
        Param (
                [string] $Path
        )
        Trap  [Exception] {
                Write-Debug $("TRAPPED: " + $_.Exception.Message);
                Write-Verbose "Could not change date on folder (Folder open in explorer?)"
                Continue
        }

        # Get latest filedate in folder
        $LatestFile = Get-ChildItem $Path | Sort-Object LastWriteTime -Descending | Select-Object -First 1

        # Change the date, if needed
        $Folder = Get-Item $path
        if ($LatestFile.LastWriteTime -ne $Folder.LastWriteTime) {
                Write-Verbose "Changing date on folder '$($Path)' to '$($LatestFile.LastWriteTime)' taken from '$($LatestFile)'"
                $Folder.LastWriteTime = $LatestFile.LastWriteTime
        }

        Return $Folder
}

Set-FolderDate -Path "D:\temp"

Unzip multiple files via Powershell

Simple and effective.

PARAM (
        [string] $ZipFilesPath = "X:\Somepath\Full\Of\Zipfiles",
        [string] $UnzipPath = "X:\Somepath\to\extract\to"
)

$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($UnzipPath)

$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP

$progress = 1
foreach ($ZipFile in $ZipFiles) {
        Write-Progress -Activity "Unzipping to $($UnzipPath)" -PercentComplete (($progress / ($ZipFiles.Count + 1)) * 100) -CurrentOperation $ZipFile.FullName -Status "File $($Progress) of $($ZipFiles.Count)"
        $ZipFolder = $Shell.NameSpace($ZipFile.fullname)

        $Location.Copyhere($ZipFolder.items(), 1040) # 1040 - No msgboxes to the user - http://msdn.microsoft.com/en-us/library/bb787866%28VS.85%29.aspx
        $progress++
}

Btw… Watch out… there is a -Recurse on gci… :-)


Next Page »