Friday, June 10, 2016

PowerShell & Google Maps Time Zone API: Check and Change Computer Time using Internet Time

I've been using WinPE to install images on new PC's and found that since Dell computers come from Texas, the Time Zone and PC clock time needs to be updated to my Time Zone. I'm probably over complicating this process but thought I'd share this anyway:

#Google's time variable is based on the start date/time of 1/1/1970, universal time zone
$startdatetime = [datetime]"1/1/1970 00:00:00Z"

#getting local time and converting to universal time zone
$currentdatetime = (get-date).touniversaltime()

#Google time variable takes the 1/1/1970 date and wants the total seconds from that date to current time
[int]$totalseconds = ($currentdatetime - $startdatetime).totalseconds

#get a server key by signing up for the Google Maps Time Zone API 
#read more here
#note: location just has to be somewhere in your area/time zone
$timezone = invoke-restmethod ("https://maps.googleapis.com/maps/api/timezone/json?location=46,-122" + "&Timestamp=" + $totalseconds + "&key=")
$timezonename = $timezone.timezonename

#since I live in Washington, I have the two possible time zones. Obviously you'll want to add/change time zones based on your location(s).
if ($timezonename -eq "Pacific Daylight Time"){$TZDiff = -7}
if ($timezonename -eq "Pacific Standard Time"){$TZDiff = -8}

#Now I'm pulling local timezone information
$tz = ([timezoneinfo]::local).id
write-host "Local Timezone setting: $tz"

#creating a variable in case the timezone is incorrect
#The ref'd timezone.ps1 script is from Peter Henchley's blog
$changetime = "e:\scripts\timezone.ps1 $timezonename"
if ($tz -ne $timezonename){powershell -executionpolicy bypass $changetime;write-host "Changed Timezone" -f yellow}

#Once the time zone has been adjusted, now we check for time
#We pull current time from www.timeapi.org, convert it to universal time, and add our time zone difference so it equals our current time zone
$webtime = (([datetime](iwr http://www.timeapi.org/utc/now -usebasicparsing).content).touniversaltime()).addhours($TZDiff)
$localhour = (get-date).hour
if ($localhour -ne $webtime.hour){set-date $webtime;write-host "Changed Local Time" -f yellow}

An example of a Google Map Time Zone API reply

dstOffset    : 3600
rawOffset    : -28800
status       : OK
timeZoneId   : America/Los_Angeles
timeZoneName : Pacific Daylight Time

Let me know if you know of a better method of checking for time zones and time without using local computers or domain servers.

PowerShell & SCCM 2012: Portable Script for Removing Computers from Collections

While in the field away from your SCCM Console, you can still remove collections from a computer via this script. Run as a user with SCCM rights or input your own $admcred and add -credential $admcred to each SCCM WMI call.

Here's an example of removing collection 4:
$ComputerName = read-host "Enter Computer Name"
$SCCMserver = "SCCMServer01"
$namespace = "root\sms\site_xx1"
$i = 0

#SCCM
#Getting list of current software collections and then filtering out unneeded selection items
if (test-connection $SCCMServer){
$xlist = import-csv \\server\share\offlist.csv #list of collection exclusions
$xlist|add-member -notepropertyname line -notepropertyvalue ""
$CollectionList = get-wmiobject -query "Select * from SMS_Collection" -namespace $namespace -computername $SCCMserver
$CollectionList = $CollectionList|? {$xlist.name -notcontains $_.name}
foreach ($C in $CollectionList){$i++;$C|add-member -notepropertyname line -notepropertyvalue $i}
}

$CurrentApps = Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter "Name = '$computername'"
$InstalledSoftwareList = $CollectionList|? {$CurrentApps.collectionID -contains $_.collectionID}

do{
write-host "SCCM Software already installed on $computername" -f white
$InstalledSoftwareList|format-table line,name -hidetableheaders
if ($removesoftware){
write-host "Software to be removed" -f yellow
$removesoftware|format-table line,name -hidetableheaders

}
write-host "Enter " -nonewline;write-host "line number" -f white -nonewline;write-host " to remove SCCM software from $computername"
write-host "Enter " -nonewline;write-host "DONE" -f white -nonewline;write-host " if satisfied with removal list"
$removepackages = read-host "(Line number or Done)"
if ($removepackages -ne "done"){
[array]$removesoftware += $collectionlist|? line -eq $removepackages
}
} while ($removepackages -ne "done")

Creates the menu:
Enter Computer Name: PCReception01
SCCM Software already installed on PCReception01

  1 Adobe Acrobat Standard DC
  2 7 Zip
  3 Google Earth Pro
  4 Microsoft Office 2013
  5 Windows Mobile Device Center

Enter line number to remove SCCM software from PCReception01 
Enter DONE if satisfied with removal list
(Line number or DONE): 4

SCCM Software already installed on PCReception01

  1 Adobe Acrobat Standard DC
  2 7 Zip
  3 Google Earth Pro
  5 Windows Mobile Device Center

Software to be removed

4 Microsoft Office 2013

Enter line number to remove SCCM software from PCReception01 
Enter DONE if satisfied with removal list
(Line number or DONE): 

And now we remove each selected collection via the $removesoftware variable:
foreach ($r in $removesoftware){
$ID = $r.collectionID
$Collection = get-wmiobject -class SMS_Collection -namespace $namespace -computername $SCCMserver|? CollectionID -eq $ID
$ruleclass = get-wmiobject -namespace $namespace -class "SMS_CollectionRuleDirect" -computername $SCCMserver -list
$RemoveComputer = get-wmiobject -ComputerName $SCCMServer -Namespace $Namespace -Class "SMS_R_System" -Filter "Name = '$($computername)'"
$DelRule = $RuleClass.CreateInstance()     
$DelRule.RuleName = $($RemoveComputer.name)
$DelRule.ResourceClassName = "SMS_R_System" 
$DelRule.ResourceID = $($RemoveComputer.resourceid)
$Collection.DeleteMemberShipRule($DelRule)
$Collection.requestrefresh()
}

Here's what you should see:
___GENUS               : 2
___CLASS               : ___PARAMETERS
___SUPERCLASS          :
___DYNASTY             : ___PARAMETERS
___RELPATH             :
___PROPERTY_COUNT      : 1
___DERIVATION          : {}
___SERVER              :
___NAMESPACE           :
___PATH                :
ReturnValue            : 0
PSComputerName         :

___GENUS               : 2
___CLASS               : ___PARAMETERS
___SUPERCLASS          :
___DYNASTY             : ___PARAMETERS
___RELPATH             :
___PROPERTY_COUNT      : 1
___DERIVATION          : {}
___SERVER              :
___NAMESPACE           :
___PATH                :
ReturnValue            : 0
PSComputerName         :

You want to see ReturnValue's of 0. Anything else and the removal wasn't successful.

PowerShell & SCCM 2012: Portable Script for Adding Computers to Collections

Being a field technician, the SCCM Console is not always nearby. The script in this post works from a thumb drive and has no need for the SCCM PowerShell module. Run this script with SCCM level credentials or you can add your username/password ($admcred) and add -credential $admcred to your SCCM WMI calls.


$SCCMserver = "SCCMServer01"
$namespace = "root\sms\site_xx1"
$computername = read-host "Enter Computer Name"
$i = 0

#SCCM
#Getting list of current software collections and then filtering out unneeded selection items
if (test-connection $SCCMServer){
$xlist = import-csv (\\server\share\offlist.csv #list of filtered out collections
$xlist|add-member -notepropertyname line -notepropertyvalue ""
$CollectionList = get-wmiobject -query "Select * from SMS_Collection" -namespace $namespace -computername $SCCMserver
$CollectionList = $CollectionList|? {$xlist.name -notcontains $_.name}
foreach ($C in $CollectionList){$i++;$C|add-member -notepropertyname line -notepropertyvalue $i} #adding line numbers to each collection entry
}else{write-host "No SCCM Server connection, ending script" -f yellow;start-sleep 10;exit}

$CurrentApps = Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter "Name = '$computername'"
$InstalledSoftwareList = $CollectionList|? {$CurrentApps.collectionID -contains $_.collectionID}

do{
cls
write-host "Available SCCM Software Packages" -f white
$collectionlist|? {$InstalledSoftwareList.name -notcontains $_.name}|format-table line,name
write-host "SCCM Software already installed on $computername" -f white
$InstalledSoftwareList|format-table line,name -hidetableheaders
if ($newsoftware){
write-host "Software to be added" -f yellow
$newsoftware|format-table line,name -hidetableheaders
}
write-host "Enter " -nonewline;write-host "line number" -f white -nonewline;write-host " to add SCCM software to $computername"
write-host "Enter " -nonewline;write-host "DONE" -f white -nonewline;write-host " if satisfied with install list"
$addpackages = read-host "(Line number or Done)"
if ($addpackages -ne "done"){
$newsoftware += $collectionlist|? line -eq $addpackages
}
} while ($addpackages -ne "done")

Here's a sample of this menu after selecting line item 7 and typing DONE at the prompt:
Available SCCM Software Packages

line name
---- ----
   1 Adobe Acrobat Pro DC
   3 Adobe Acrobat Standard DC
   4 Base Camp
   5 Google Earth Pro

SCCM Software already installed on PCReception01

   2 Adobe Acrobat Reader DC
   6 7 Zip

Software to be added

7 Windows Mobile Device Center

Enter line number to add SCCM software to PCReception01
Enter DONE if satisfied with installation list
(Line number or DONE): DONE

Now we add $computername to the selected collection via the $newsoftware variable. Let's create a new rule that adds the computer it to the collection:
foreach ($c in $newsoftware){
$ID = $c.collectionID
$Collection = get-wmiobject -class SMS_Collection -namespace $namespace -computername $SCCMserver|? CollectionID -eq $ID
$ruleclass = get-wmiobject -namespace $namespace -class "SMS_CollectionRuleDirect" -computername $SCCMserver -list
$AddComputer = get-wmiobject -ComputerName $SCCMServer -Namespace $Namespace -Class "SMS_R_System" -Filter "Name = '$($computername)'"
$NewRule = $RuleClass.CreateInstance()     
$NewRule.RuleName = $($AddComputer.name)
$NewRule.ResourceClassName = "SMS_R_System" 
$NewRule.ResourceID = $($AddComputer.resourceid)
$Collection.AddMembershipRules($newrule)
$Collection.requestrefresh()
}

And here's how that looks:
___GENUS               : 2
___CLASS               : ___PARAMETERS
___SUPERCLASS          :
___DYNASTY             : ___PARAMETERS
___RELPATH             :
___PROPERTY_COUNT      : 2
___DERIVATION          : {}
___SERVER              :
___NAMESPACE           :
___PATH                :
QueryIDs               : {0}
ReturnValue            : 0
PSComputerName         :

___GENUS               : 2
___CLASS               : ___PARAMETERS
___SUPERCLASS          :
___DYNASTY             : ___PARAMETERS
___RELPATH             :
___PROPERTY_COUNT      : 1
___DERIVATION          : {}
___SERVER              :
___NAMESPACE           :
___PATH                :
ReturnValue            : 0
PSComputerName         :


You want the returnvalue to equal 0.  This means the rule successfully added your computer to the collection.