Thursday, August 11, 2016

PowerShell: Gmailing / Emailing Screenshots

I totally scraped the script from AdminAresenal. Kris Powell wrote such a good solution that I lifted it and added a gmailer process. I'm posting this solution in case someone needs to automate emailing of screenshots. The automation component is Task Scheduler.  My scheduled job uses "powershell -executionpolicy unrestricted -windowstyle minimized c:\scripts\screenshots.ps1" so I don't have the PowerShell script window appear in each screenshot.

#Create a timestamp-based BMP filename

$filename = (get-date).tostring().replace('/','.').replace(':','.')

#Create a folder for your outputted screenshots
$File = "c:\scripts\ss\$filename.bmp"

## From the AdminArsenal blog ##
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing

# Gather Screen resolution information
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$Width = $Screen.Width
$Height = $Screen.Height
$Left = $Screen.Left
$Top = $Screen.Top

# Create bitmap using the top-left and bottom-right bounds
$bitmap = New-Object System.Drawing.Bitmap $Width, $Height

# Create Graphics object
$graphic = [System.Drawing.Graphics]::FromImage($bitmap)

# Capture screen
$graphic.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)

# Save to file
$bitmap.Save($File) 

#Now we send that screenshot via GMail
#Enable "Less Secure Apps" in order to use SMTP via script
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "yourgmailaccount@gmail.com"
$Password = "P@$$w0rd"

$to = "addressee@gmail.com"
$cc = "anotherperson@someotherdomain.com"
$subject = "Screenshot taken $filename"
$body = "Screenshot Attachment"
$attachment = $file

$message = New-Object System.Net.Mail.MailMessage
$message.subject = $subject
$message.body = $body
$message.to.add($to)
$message.cc.add($cc)
$message.from = $username
$message.attachments.add($attachment)

$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)

Sunday, July 31, 2016

PowerShell: Manipulate Workstation Software Information via SCCM

During the computer replacement cycle, a typical chore is retrieving the software list from the old computer and ensuring its replacement computer gets said software.  If SCCM packages are involved, there are two sources to check during this process: The local repository and the SCCM assigned repositories.

To help with this process, as well as other similar software management functions, I created a script which collects the existing computers SCCM packages and transfers them to the new computer, views SCCM's list of local software, removes a SCCM package from a computer, edit/update the SCCM list table, and change computer numbers so you can look at another computer without leaving the script.

Now, this script, just like my others, are works in progress.  As such, they might have errors in your environment.  I wanted to share what I have in case it might help others with their daily duties.  Please let me know if you have improvements.

$tagnumber = read-host "Enter Computer Tag"
$computername = $tagnumber.toupper()
$SCCMserver = "DomainSCCMServer01"
$namespace = "root\sms\site_xx1"
$Server = "DomainGCServer01"
$i = 0

#pulling in existing administrative credentials for $admcred variable
if (test-path \\server\share\it_tools\1p.eee){
$name = "domain\admin01"
$password = get-content \\server\share\it_tools\1p.eee|convertto-securestring -key (1..16)
$admcred = new-object system.management.automation.pscredential($name,$password)
}

#Testing if the admin password has expired
$ServerTest = new-psdrive -name test -root \\DomainSCCMServer01\client -credential $admcred -psprovider FileSystem
#Testing if 1p.eee password file exists
$PWTest = test-path \\server\share\it_tools\1p.eee

#if either of the above commands fail, the admin gets prompted to update/add their admin credential
if (($PWTest -ne $true) -or ($ServerTest.name -ne "test")){
$admcred = get-credential -username "domain\adm_youraccount" -message "Update your ADM Password"
$password = $admcred.password|convertfrom-securestring -key (1..16)
$password|out-file \\server\share\it_tools\1p.eee
$password = gc \\server\share\it_tools\1p.eee|convertto-securestring -key (1..16)
$admcred = new-object system.management.automation.pscredential($name,$password)
}
remove-psdrive test


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

do{
#SCCM Collection Query
$CurrentApps = Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter "Name = '$computername'" -credential $admcred

#Applying current computer software list to complete software list
$InstalledSoftwareList = $CollectionList|? {$CurrentApps.collectionID -contains $_.collectionID}

#Querying SCCM for main user of selected computer
$users = gwmi -computername $sccmserver -namespace $namespace -class sms_usermachinerelationship -filter "resourcename = '$computername'"
$userlist = $users|% {get-aduser $_.uniqueusername.split('\')[1]|select givenname,surname,name}

write-host "Available SCCM Software Packages" -f white
$collectionlist|? {$InstalledSoftwareList.name -notcontains $_.name}|format-table line,name -autosize -hidetableheaders
write-host ("Current SCCM Packages for " + $computername + ":") -f white
$InstalledSoftwareList|format-table line,name -hidetableheaders -autosize

#Adds the following if a software from the collection has been added to the install list
if ($newsoftware){
write-host "Software to be added" -f yellow
$newsoftware|format-table line,name -hidetableheaders -autosize
}

#Shows users of selected computer
write-host "User Profiles on $computername" -f white
$userlist|format-table -autosize -hidetableheaders

#Menu
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 "LOCAL" -f white -nonewline;write-host " to view local software on $computername"
write-host "Enter " -nonewline;write-host "REMOVE" -f white -nonewline;write-host " to remove SCCM software from $computername"
write-host "Enter " -nonewline;write-host "EDIT" -f white -nonewline;write-host " to update your SCCM Package Filter"
write-host "Enter " -nonewline;write-host "TRANSFER" -f white -nonewline;write-host " to add $computername software to another computer"
write-host "Enter " -nonewline;write-host "CHANGE" -f white -nonewline;write-host " to change computer"
if ($transfername){
write-host ($transfername + " SCCM App List:")
$installedapps.name}

write-host "Enter " -nonewline;write-host "DONE" -f white -nonewline;write-host " to add software and finish script"
$addpackages = read-host "(Line number, Local, Remove, Edit, Transfer, Change, or Done)"

## CHANGE ##
if ($addpackages -eq "change"){
$tagnumber = read-host "Enter Computer Tag"
$computername = $tagnumber.toupper()
}

## LOCAL ##
if ($addpackages -eq "local"){
$i = 0
$localxlist = import-csv \\server\share\it_tools\offlocalsoftwarelist.csv
$oldcomputerlocalsoftware = gwmi -computername $sccmserver -namespace $namespace -Query ("select InstalledLocation, ProductVersion, ProductName from SMS_R_System join SMS_G_SYSTEM_Installed_Software on SMS_R_System.ResourceID = SMS_G_SYSTEM_Installed_Software.ResourceID where SMS_R_SYSTEM.Name= ""$($computername)"" ") -credential $admcred
$oldcomputerlocalsoftware = $oldcomputerlocalsoftware|? {$localxlist.productname -notcontains $_.productname -and $localxlist.productversion -notcontains $_.productversion}
foreach ($C in $oldcomputerlocalsoftware){$i++;$C|add-member -notepropertyname line -notepropertyvalue $i}

do{
#local software menu.  It also has a filter to exclude unwanted software from its list
write-host "`nLocal software found on $SCCMOldComputer"
$oldcomputerlocalsoftware|format-table line,productname,productversion -hidetableheaders -autosize
if ($removesoftware){write-host "Will be added to local filter list:" -f green}
if ($removesoftware){$removesoftware|format-table -hidetableheaders -autosize}
write-host "Enter " -nonewline;write-host "line number" -f white -nonewline;write-host " to update local software exclusion filter"
write-host "Enter " -nonewline;write-host "Return" -f white -nonewline;write-host " if list is correct"
$addtolxlist = read-host "(Line number or Return)"
if ($addtolxlist -ne "return"){
[array]$removesoftware += $oldcomputerlocalsoftware|? line -eq $addtolxlist|select productname,productversion
$oldcomputerlocalsoftware = $oldcomputerlocalsoftware|? {$removesoftware.productname -notcontains $_.productname -and $removesoftware.productversion -notcontains $_.productversion}
}
} while ($addtolxlist -ne "return")
if ($removesoftware){
$localxlist = $localxlist|select ProductName,ProductVersion
$localxlist += $removesoftware|select ProductName,ProductVersion
$localxlist|export-csv \\server\share\it_tools\offlocalsoftwarelist.csv
$removesoftware = $nul
}

}

## REMOVE ##
if ($addpackages -eq "remove"){

do{
#cls
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 "REMOVE" -f white -nonewline;write-host " if satisfied with removal list"
$removepackages = read-host "(Line number or Remove)"

if ($removepackages -ne "remove"){
[array]$removesoftware += $collectionlist|? line -eq $removepackages
}

} while ($removepackages -ne "Remove")

if ($removesoftware){
foreach ($r in $removesoftware){
$ID = $r.collectionID
$Collection = gwmi -class SMS_Collection -namespace $namespace -computername $SCCMserver -credential $admcred|? CollectionID -eq $ID
$ruleclass = gwmi -namespace $namespace -class "SMS_CollectionRuleDirect" -computername $SCCMserver -list -credential $admcred
$RemoveComputer = gwmi -ComputerName $SCCMServer -Namespace $Namespace -Class "SMS_R_System" -Filter "Name = '$($computername)'" -credential $admcred
$DelRule = $RuleClass.CreateInstance()     
$DelRule.RuleName = $($RemoveComputer.name)
$DelRule.ResourceClassName = "SMS_R_System" 
$DelRule.ResourceID = $($RemoveComputer.resourceid)
$Collection.DeleteMemberShipRule($DelRule)
$Collection.requestrefresh()
}
}

## EDIT ##
if ($addpackages -eq "edit"){
do{
write-host "Current SCCM Package Filter" -f white
$CollectionList|format-table line,name -hidetableheaders -autosize
if ($updatexlist){write-host "Will be added to filter:" -f green}
if ($updatexlist){$updatexlist|format-table -hidetableheaders -autosize}
write-host "Enter " -nonewline;write-host "line number" -f white -nonewline;write-host " to remove from list"
write-host "Enter " -nonewline;write-host "Update" -f white -nonewline;write-host " to execute filter update"
$addtoxlist = read-host "(Line number or Update)"
if ($addtoxlist -ne "update"){
[array]$remove += $CollectionList|? line -eq $addtoxlist|select name,collectionID
[array]$UpdateXList = $CollectionList|? line -eq $addtoxlist|select line,name
}
} while ($addtoxlist -ne "update")
#saving updated collectionlist

if ($remove){
$xlist = $xlist|select Name,CollectionID
$xlist += $remove|select Name,CollectionID
$xlist = $xlist|sort CollectionID -unique
$xlist|export-csv \\server\share\it_tools\offlist.csv
write-host "Filter updated" -f green}
}
}

## TRANSFER ##
if ($addpackages -eq "transfer"){
$transfercomputer = read-host "Transfer to Tag Number"
$transfername = $transfercomputer.toupper()
foreach ($c in $InstalledSoftwareList){
$ID = $c.collectionID
$Collection = gwmi -class SMS_Collection -namespace $namespace -computername $SCCMserver -credential $admcred|? CollectionID -eq $ID
$ruleclass = gwmi -namespace $namespace -class "SMS_CollectionRuleDirect" -computername $SCCMserver -list -credential $admcred
$AddComputer = gwmi -ComputerName $SCCMServer -Namespace $Namespace -Class "SMS_R_System" -Filter "Name = '$($transfername)'" -credential $admcred
$NewRule = $RuleClass.CreateInstance()     
$NewRule.RuleName = $($AddComputer.name)
$NewRule.ResourceClassName = "SMS_R_System" 
$NewRule.ResourceID = $($AddComputer.resourceid)
$Collection.AddMembershipRules($newrule)
$Collection.requestrefresh()
}
$CheckApps = Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter "Name = '$transfername'" -credential $admcred
$InstalledApps = $CollectionList|? {$CheckApps.collectionID -contains $_.collectionID}
}

if (($addpackages -ne "done") -and ($addpackages -ne "edit") -and ($addpackages -ne "transfer")){
[array]$newsoftware += $collectionlist|? line -eq $addpackages
}
} until ($addpackages -eq "done")


#Added packages will get processed after DONE has been entered
if ($newsoftware){
foreach ($c in $newsoftware){
$ID = $c.collectionID
$Collection = gwmi -class SMS_Collection -namespace $namespace -computername $SCCMserver -credential $admcred|? CollectionID -eq $ID
$ruleclass = gwmi -namespace $namespace -class "SMS_CollectionRuleDirect" -computername $SCCMserver -list -credential $admcred
$AddComputer = gwmi -ComputerName $SCCMServer -Namespace $Namespace -Class "SMS_R_System" -Filter "Name = '$($computername)'" -credential $admcred
$NewRule = $RuleClass.CreateInstance()     
$NewRule.RuleName = $($AddComputer.name)
$NewRule.ResourceClassName = "SMS_R_System" 
$NewRule.ResourceID = $($AddComputer.resourceid)
$Collection.AddMembershipRules($newrule)
$Collection.requestrefresh()
$CheckApps = Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter "Name = '$computername'" -credential $admcred
$InstalledApps = $CollectionList|? {$CheckApps.collectionID -contains $_.collectionID}
$AddedApps = $Installedapps|? {$InstalledSoftwareList.collectionID -notcontains $_.collectionID}
[array]$apps = $Addedapps.name
write-host ("This software has been added to $Computername successfully: ")
($Installedapps|? {$InstalledSoftwareList.collectionID -notcontains $_.collectionID}).name
}
}

Saturday, July 2, 2016

PowerShell: Using DNS and Printer CSV List for IP-Based Printer Installs on Random Network Environments

I support incident management teams and have been trying to find a simple-ish way to have users install printers no matter our network environment.  I setup the same server every incident so that is where I place all scripts and drivers.  We perform massive printing in a short amount of time and this can cause printers to be replaced as they wear out.  Instead of visiting every networked laptop to update printers, I now have 10 static printer numbers and use a CSV file to give driver information, IP address, and printer name.  The user locates the nearest printer, double-clicks the installation batch file, and the process removes the old driver and IP address of that printer and installs the current settings.  This process has been tested for PowerShell version 4.0.
Steps in creating this printer installation process:
  1. Remote into each printer and setup a DNS name.  Ping the IP and DNS name after making the change.  Ensure you allow the printer to take the DNS suffix of the local DHCP server.  Use DHCP.
  2. Download and test the driver that'll be used.  Find the correct INF file that has the printer driver name.  You'll have to add the printer driver name and driver shared folder location to the printerlist.csv file.
  3. Label your printers so users know which printer they need to install.
  4. Create your batch files which start the install process:

@CLS
@powershell -command "start-process powershell" -verb runas -argumentlist '-ExecutionPolicy bypass -file \\server\printers\InstallPrinter.ps1 Team12P1'
    • @cls clears screen to get rid of extraneous messages while starting script
    • @powershell -command "start-process powershell" -verb runas elevates PowerShell to runas Administrator and bypass the UAC settings which can sometimes stop driver installations
    • -argumentlist '-ExecutionPolicy bypass -file \\server\printers\InstallPrinter.ps1 Team12P1' bypasses any PS script execution policy setting that may be set on that computer.  Then it runs the InstallPrinter.ps1 script and passes the Team12P1 variable to that script so it can look up the printer in the CSV file. Create separate batch files for each printer and change the variable to the DNS name of that printer.
    1. Create and input printer information into the printerlist.csv file.  Here is a sample:

    DNSName,DriverShare,DriverINF,DriverName,LastKnownIP,MakeModel,Supplies,Notes
    Team12P1,\\Team12server\printers\Drivers\HPOJP8630,hpvyt13.inf,HP Officejet Pro 8630,192.168.1.100,HPOJP8630,951XL,
    Team12P2,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP400MFP,80X,
    Team12P3,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP400MFP,80X,
    Team12P4,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP400MFP,80X,
    Team12P5,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP4015,64A,
    Team12P6,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJP4015,64A,
    Team12P7,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,192.168.1.100,HPLJ600,90A,
    Team12P8,\\Team12server\printers\Drivers\HPOJP8630,hpvyt13.inf,HP Officejet Pro 8630,192.168.1.100,HPOJP8630,951XL,
    ,,,,,,,
    Team12P10,\\Team12server\Printers\Drivers\HPCLJ5520,hpc5520u.inf,HP Color LaserJet CP5520 Series PCL 6,192.168.1.100,HPCLJ5525,650A,USB and WiFi Only
    Team12L1,\\Team12server\printers\Drivers\HPUniversalPCL5,hpcu180t.inf,HP Universal Printing PCL 5,,HPLJ1102W,85A,
    Team12L2,,,,,EpsonWF3640,Epson252XL(BCYM),Ordering Manager
    
    • DNSName: Printer name DNS will use to search for an IP
    • DriverShare: Printer driver UNC folder location
    • DriverINF: Printer driver INF file located at the DriverShare location
    • DriverName: Exact printer name found within the Driver INF file
    • LastKnownIP: Static IP used to create a port when the printer and laptop are on different networks. You will have to create a new IP Port when the laptop is on the same network as the printer.
    • MakeModel: Not used in scripts but for your reference
    • Supplies: Not used in scripts but place to note what print cartridges each printer needs
    • Notes: Not used in scripts but provides general information
    1. Create the InstallPrinter.ps1 file referenced in the initial printer installation batch file.  Here is the script I use:

    #No 32-bit support
    $DNSName = $args
    $printerlist = import-csv \\server\printers\installscripts\printerlist.csv|? DNSName -eq $DNSName
    $DriverShare = $printerlist.drivershare
    $DriverINF = $printerlist.driverinf
    $DriverName = $printerlist.drivername
    $LastKnownIP = $printerlist.lastknownIP
    $IP = $LastKnownIP
    
    #Checking for old version of printer, then deleting it so new version can be added
    if ((gwmi win32_service|? name -match spool|select -expand state) -eq 'Stopped'){start-service spooler}
    if ((gwmi win32_printer|select -expand name) -contains $DNSName){
    #This will remove any spooled jobs then delete the printer with the old IP address
    if ((gwmi win32_service|? name -match spool|select -expand state) -eq 'running'){stop-service spooler}
    remove-item c:\windows\system32\spool\printers\*.* -force
    do {sleep 1}until((gwmi win32_service|? name -match spool|select -expand state) -eq 'Stopped')
    start-service spooler
    sleep 5
    $settings = gwmi win32_printer|? name -eq $DNSName|select *
    write-host "`nDeleting old printer installation`n"
    cscript c:\windows\system32\printing_admin_scripts\en-us\prnmngr.vbs -d -p $DNSName
    $Oldport = $settings.portname
    write-host "`nDeleting old Port: $Oldport`n" -f blue
    $portmsg = cscript c:\windows\system32\printing_admin_scripts\en-us\prnport.vbs -d -r $Oldport
    $d = cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -x
    }
    
    write-host ("Printer: " + $DNSName)-f green
    if ($IP.count -gt 0){write-host ("IP address: " + $IP)-f green}else{write-host ("No Active IP, using last known IP: " + $IP)-f red}
    write-host ("`n$DNSName will be available once this window disappears (approx. 3 minutes).`n") -f yellow
    $delprinters = cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -x
    cscript c:\windows\system32\printing_admin_scripts\en-us\prndrvr.vbs -a -m $DriverName -h $DriverShare -i ($DriverShare + "\" + $DriverINF)
    cscript c:\windows\system32\printing_admin_scripts\en-us\prnport.vbs -a -r $IP -h $IP -o raw -n 9100
    #rundll32 printui.dll,PrintUIEntry /if /b $DNSName /r $IP /m $DriverName
    cscript c:\windows\system32\printing_admin_scripts\en-us\prnmngr.vbs -a -p $DNSName -m $DriverName -r $IP
    if ((gwmi win32_printer|select -expand name) -contains $DNSName){write-host "`n$DNSName installed correctly!" -f green;sleep 5}
    

    Now you have a generic printer installation process that you can easily alter when you need to update IPs, Printer Names, or Printer Driver information.  As long as you've renamed your printers DNS names correctly, they should be found on any network subnet you share.  Also, the printerlist.csv file is easy to load into Excel and use for other things besides your installation scripts.

    Last note: In my scenario, I have this system connected to a Box account so all changes are backed up to the cloud and I can update these files from the cloud outside the local subnet/network.

    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.