Waking up a SCCM collection from vbscript.

I wanted to wake up all the machines in a collection using a vbscript. I know that SCCM has this built in, but I could not get it working. To troubleshoot I figured I would write a script to get collection members, and then wake them via the command line with this tool: http://www.gammadyne.com/cmdline.htm#wol

GetCollectionMembers "XXX00018"

Sub GetCollectionMembers (COLLECTION_NAME)
  Set objLocation = CreateObject("WbemScripting.SWbemLocator")
  Set objService = objLocation.ConnectServer("SERVERNAME", "root\SMS\site_XXX")
  strQuery = "SELECT * FROM SMS_FullCollectionMembership WHERE CollectionID = '" & COLLECTION_NAME & "'"
  Set objSourceCollectionMembers = objService.ExecQuery(strQuery)
  For Each Resource In objSourceCollectionMembers
	WakeMachine objService,Resource.ResourceID
  Next
End Sub

Sub WakeMachine (objService,ResourceID)
  Set Machines = objService.ExecQuery("Select * From SMS_R_System where ResourceID =" & ResourceID)
  For Each Machine In Machines
	Set objShell = CreateObject("Wscript.Shell")
	strCurrentDir = Replace(WScript.ScriptFullName,WScript.ScriptName,"")
	strCommand = strCurrentDir & "\wol.exe " & Replace(Machine.MACAddresses(0),":","")
	Set objExecObject = objShell.Exec(strCommand)
  Next
End Sub

SCCM “trickle” install.

We wanted to deploy software to our environment via and assigned advertisement in SCCM, but we wanted to be able to install packages to a subset of a collection. If there is an issue the next day, the whole enterprise would not down. We already had a collection that identified machines that need the package, we just want to deploy to the first 15 one day, and another 15 the next day.

Since WQL does not allow a SQL “TOP” I did not think I would be able to do it via a complex query. So I wrote the following vbscript to find machines in one Collection and add them to another collection:

Sub CopyMachinesToCollection (SOURCE_COLLECTION,TARGET_COLLECTION,ResourcesAtATime)
	Set objLocation = CreateObject("WbemScripting.SWbemLocator")
	Set oService = objLocation.ConnectServer("server", "root\SMS\site_XXX")

	Set oSourceCollectionMembers = oService.ExecQuery("SELECT ResourceID, Name FROM SMS_FullCollectionMembership WHERE CollectionID = '" & SOURCE_COLLECTION & "'")
	Set oTargetCollection = oService.Get("SMS_Collection.CollectionID='" & TARGET_COLLECTION & "'")

	' Add ResourcesAtATime resources to
	counter=0
	For Each Resource In oSourceCollectionMembers
		if counter < ResourcesAtATime then
			'Wscript.Echo Resource.ResourceID  & "-" & Resource.Name
			Set DirectRule = oService.Get("SMS_CollectionRuleDirect").SpawnInstance_()
			DirectRule.ResourceClassName = "SMS_R_System"
			DirectRule.ResourceID = Resource.ResourceID
			DirectRule.RuleName = Resource.Name
			oTargetCollection.AddMembershipRule DirectRule, SMSContext
			oTargetCollection.RequestRefresh False
			end if
			counter=counter+1
	Next
End Sub

Sub DeleteTargetCollection (TARGET_COLLECTION)
	Set objLocation = CreateObject("WbemScripting.SWbemLocator")
	Set oService = objLocation.ConnectServer("svnyem01", "root\SMS\site_SVC")

	Set oTargetCollection = oService.Get("SMS_Collection.CollectionID='" & TARGET_COLLECTION & "'")

	' Delete all in oTargetCollection
	If Not IsNull(oTargetCollection.CollectionRules) Then
		For Each Rule In oTargetCollection.CollectionRules
			wscript.echo Rule.RuleName
			oTargetCollection.DeleteMembershipRule Rule
		Next
		oTargetCollection.RequestRefresh False
	End If
End Sub

Second sub removes all machines from the collection, and the first copies the first “x” from the soure to the destination

iPad app pricing?

Just was reading this article. I couldn’t agree more with this statement. It costs equal or more to but the WSJ on the iPad?

Compare the strategies and thinking. On the one hand we have a set of pricing models that deliver marginal value for premium prices and show very little that differentiate themselves from the web experience, although they expect to charge more. These pricing models are based on a sense of entitlement to set pricing as it was in the days of print. I won’t even call them strategies because they lack any kind of realistic strategic thinking

Powershell signing, makecert.exe and group policy – Part 2

In the previous post I showed how to create self signed signing certs via the makecert tool. Once I made the pfx files, and signed my script, I need to make the server I was working see the certificate chain. I created a GPO and applied it to the server OU. I added the Root cert to Trusted Root Certification Authorities, and I added the Cert to Trusted Publishers Certificates. Now when I open a signed script on a server, I no longer have to change the Set-ExecutionPolicy RemoteSigned and add the network path to the Local Intranet zone.

Powershell signing, makecert.exe and group policy – Part 1

We don’t have a pki infrastructure at my current job, and I like to keep my powershell scripts on my network drive. So I needed a way to run my .ps1 files off a network drive. Setting Set-ExecutionPolciy to Unrestricted seemed like a bad idea. So I looked into using a signed script and setting Set-ExecutionPolicy to RemoteSigned. I could not justify $300 to buy a trused third party cert, so I looked at makecert.exe from the Windows SDK.

First step make the Root CA cert, private key, and pxf.

  1. makecert -n “CN=RootName” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv Root.pvk Root.cer
  2. pvk2pfx.exe -pvk Root.pvk -spc Root.cer -pfx Root.pfx -pi password

makecert and pvk2pfx can be found in the Windows SDK or in the Visual Studio bin directory. First command results in a private key (.pvk) and a certificate (.cer). Second command makes a pfx out of the first 2.

Second step is to create a certificate from the root cert above.

  1. makecert -pe -n “CN=Certificate” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -ic Root.cer -iv Root.pvk -sv Certificate.pvk Certificate.cer
  2. pvk2pfx.exe -pvk Certificate.pvk -spc Certificate.cer -pfx Certificate.pfx -pi password

Now we are ready to sign our powershell script.

  1. $cert = Get-PfxCertificate Certificate.pfx
  2. Set-AuthenticodeSignature -Filepath script.ps1 -Cert $cert

Probably can be combined into one line, but I am not a guru yet.

Next post will talk about how I added this certs above to all the servers.

New MD5 based backup script

I found this use of md5 and find the other day. I based my current backup script around it. The md5 will show if anyone modifies a file, or adds/removes a file in the web hosting root (/var/www) or in the config directory (/etc/httpd/conf.d/). If there is a change then zip each site up individually and move to a backup folder to be rsynced to other servers.

NewWWWMD5=$(find /var/www/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}')
OldWWWMD5=$(cat $PARENTDIR/_var_www_*.md5)
NewConfMD5=$(find /etc/httpd/conf.d/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}')
OldConfMD5=$(cat $PARENTDIR/_etc_httpd_conf.d_*.md5)

if [ $NewWWWMD5 = $OldWWWMD5 -a $NewConfMD5 = $OldConfMD5 ]; then
	echo "Neither /var/www/ nor /etc/httpd/conf.d/ have changed"
else
	rm -rf $BACKUPDIR/*Files
	echo "/var/www or /etc/httpd/conf.d has changed"
	mkdir -p $BACKUPDIR-Files

	# backup /var/www
	for directory in /var/www/*; do
	  	if [ -d $directory ]; then
	    	bu $directory;
	    fi
	done

	# replace previous /var/www MD5
	rm -f $PARENTDIR/_var_www_*.md5
	find /var/www/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}' > $PARENTDIR/_var_www_$CURRENTDAY.md5

	#backup /etc/httpd/conf.d
	bu "/etc/httpd/conf.d"

	# replace previous /etc/httpd/conf.d MD5
	rm -f $PARENTDIR/_etc_httpd_conf.d_*.md5
	find /etc/httpd/conf.d/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}' > $PARENTDIR/_etc_httpd_conf.d_$CURRENTDAY.md5
fi

Seems to work!

PowerShell is great

Okay, I drank the Kool-Aid. PowerShell is awesome. The first thing I wanted to do was make sure my $profile is always the most current no matter what machine was on. In the past I had a batch file that I would run that would open my command prompt the way I wanted. I modified that so if PowerShell is installed, then get my profile up-to-date and then launch PowerShell. This is part of my current batch file:

IF NOT EXIST "c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" GOTO NO_PS
powershell -NoProfile $a=(Split-Path $profile -parent);if (!(Test-Path $a)) {New-Item $a -type directory};
powershell -NoProfile if (!(Test-Path $profile)) {Copy-Item %~dp0Scripts\Microsoft.PowerShell_profile.ps1 (Split-Path $profile -parent)}
powershell -NoProfile if (!(Compare-Object $(Get-Content $profile) $(Get-Content Scripts\Microsoft.PowerShell_profile.ps1)).Count -eq 0 ) {Copy-Item Scripts\Microsoft.PowerShell_profile.ps1 (Split-Path $profile -parent)}
@start  %COMSPEC% /K PowerShell -nologo

This will copy over my profile if it does not exist or is a different version. Now when I am on a server with PowerShell I can double click my batch file and my environment is up to date!