Intune: Automatically set timezone on new device build

In Michael Niehaus’ recent blog on Configuring Windows 10 defaults via Windows Autopilot using an MSI he talked about the ability to set the Time Zone of the device based on a variable in the Config.xml file. One of the comments on the blog asked whether it would be possible to set the time zone based on where the device was at the time of setup rather than based on an attribute in the file.

Whilst Windows 10 has a feature to detect the time zone automatically I have found on occasion with devices that this doesn’t always work out of the box and the time zone remains in a region that is not accurate for where the device is. This should be relatively simple to automate if we can geo-locate the public IP address and match those coordinates to a valid time zone. For this I am using two web services:

  1. https://ipstack.com – this will map the public IP address to a geo-location including coordinates, you get access to 10,000 queries a month for free
  2. Bing Maps API – this will return the correct time zone in a valid Windows format that can be used in the script to set the time zone on the device, there is a free tier here that also gives you 10,000 queries per month without charge

The Script

The script will execute the following in sequence:

  1. Attempt to get the coordinates of the IP address you are using via the IPStack API
  2. If successful, attempt to find the time zone from the Bing Maps API
  3. Compare the value of the current time zone on the machine with the response from the Bing Maps API
  4. Change the time zone on the machine

The only variable you should need to change in the script are the two lines that will contain your API keys helpfully called ipStackAPIKey and bingMapsAPIKey.

You can choose now to either save the file as a script to run once in Intune, or, incorporate this into Michael’s MSI. If you wanted the script to run every time the machine starts up, you could adapt the Logon script from my recent post on Mapping legacy files shares for Azure AD joined devices.

<#
    SCRIPT: Set-TimeZoneGeoIP.ps1
    DESCRIPTION: Based on the public egress IP obtain geoLocation details and match to a time zone
                 Once obtained set the Time Zone on the destination computer
#>

<#
    SETUP ATTRIBUTES
#>
$logFile = "$env:ProgramData\Intune-PowerShell-Logs\TimeZoneAPI-" + $(Get-Date).ToFileTimeUtc() + ".log"
Start-Transcript -Path $LogFile -Append


$ipStackAPIKey = "##########" #used to get geoCoordinates of the public IP. get the API key from https://ipstack.com
$bingMapsAPIKey = "##########" #Used to get the Windows TimeZone value of the location coordinates. get teh API key from https://azuremarketplace.microsoft.com/en-us/marketplace/apps/bingmaps.mapapis

<#
    Attempt to get Lat and Long for current IP
#>

Write-Output "Attempting to get coordinates from egress IP address"
try {
    $geoIP = Invoke-RestMethod -Uri "http://api.ipstack.com/check?access_key=$($ipStackAPIKey)" -ErrorAction SilentlyContinue -ErrorVariable $ErrorGeoIP
}
Catch {
    Write-Output "Error obtaining coordinates or public IP address, script will exit"
    Exit
}

Write-Output "Detected that $($geoIP.ip) is located in $($geoIP.country_name) at $($geoIP.latitude),$($geoIP.longitude)"
Write-Output "Attempting to find Corresponding Time Zone"
try {
    $timeZone = Invoke-RestMethod -Uri "https://dev.virtualearth.net/REST/v1/timezone/$($geoIP.latitude),$($geoIP.longitude)?key=$($bingMapsAPIKey)" -ErrorAction Stop -ErrorVariable $ErrortimeZone
}
catch {
    Write-Output "Error obtaining Timezone from Bing Maps API. Script will exit"
    Exit
}
$correctTimeZone = $TimeZone.resourceSets.resources.timeZone.windowsTimeZoneId
$currentTimeZone = $(Get-TimeZone).id
Write-Output "Detected Correct time zone as $($correctTimeZone), current time zone is set to $($currentTimeZone)"
if ($correctTimeZone -eq $currentTimeZone) {
    Write-Output "Current time zone value is correct"
}
else {
    Write-Output "Attempting to set timezone to $($correctTimeZone)"
    Set-TimeZone -Id $correctTimeZone -ErrorAction SilentlyContinue -ErrorVariable $ErrorSetTimeZone
    Write-Output "Set Time Zone to: $($(Get-TimeZone).id)"
}

Stop-Transcript

The script will output to a folder in %PROGRAMDATA% called Intune-PowerShell-Logs.

Hopefully you will find this useful to configure time zone information on your Modern Workplace machines.

Reporting on inactive AD user accounts

In the second quick article following my reporting requirement this time is to report on the enabled user accounts that have not logged in in the past X days. Again a quick Google came across the following article on WindowsITPro (Use Get-ADUser to Find Inactive AD Users)

I took the Search-ADAccount cmdlet and created some filters to exclude disabled accounts as well as enable a parameter to be passed with the script to specify the maximum age, in days, of a user account (default is 90 days)

Save the below script as Get-InactiveAccounts.ps1

Param(
    [int]$InactiveDays = 90
)
#Configure Output File
$myDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$timestamp = Get-Date -UFormat %Y%m%d-%H%M
$random = -join(48..57+65..90+97..122 | ForEach-Object {[char]$_} | Get-Random -Count 6)
$reportfile = "$mydir\InactiveAccounts-$timestamp-$random.csv"
Import-Module ActiveDirectory

Search-ADAccount -UsersOnly -AccountInactive -TimeSpan "$InactiveDays" | `
Get-ADUser -Properties Name, sAMAccountName, givenName, sn, userAccountControl,lastlogondate | `
Where {($_.userAccountControl -band 2) -eq $False} | Select Name, sAMAccountName, givenName, sn,LastLogonDate | `
Export-Csv $reportfile -NoTypeInformation

Write-Host -ForegroundColor White "Report written to $reportfile in current path."
Get-Item $reportfile

To execute the script run .\Get-InactiveAccounts.ps1 to report on accounts older than 90 days or use the InactiveDays parameter to specify the age of accounts to report (eg .\Get-InactiveAccounts.ps1 -InactiveDays 180)

Reporting on AD users last password change

As part of some recent work to assist a client with reporting on their active users and the dates those users last changed their passwords I evolved a script written by Carl Gray here (PowerShell: Get-ADUser to retrieve password last set and expiry information) to generate a short PowerShell script that will report the enabled Active Directory users and the date that they last set their password.

Copy the code below and save on your server as Get-PasswordLastChange.ps1 and then run from the command line. Script will produce a CSV file and save it in the same directory as the script

#Configure Output File
$myDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$timestamp = Get-Date -UFormat %Y%m%d-%H%M
$random = -join(48..57+65..90+97..122 | ForEach-Object {[char]$_} | Get-Random -Count 6)
$reportfile = "$mydir\PasswordLastSet-$timestamp-$random.csv"
Import-Module ActiveDirectory

Get-ADUser -filter * -properties passwordlastset, passwordneverexpires | `
Where {($_.userAccountControl -band 2) -eq $False} | `
sort-object name | `
select-object Name, passwordlastset, passwordneverexpires | `
Export-csv -path $reportfile -NoTypeInformation

Write-Host -ForegroundColor White "Report written to $reportfile in current path."
Get-Item $reportfile