Setting Locale and Timezone in Bulk

Hi All,

I’ve recently come up with a way to set the Locale and Timezone settings for users in SCSM in bulk via PowerShell and thought I would share it with you all. It’s based on the post by Travis over at the engineering team blog here, and I’ve added some things so it will set the timezone and locale settings for users already in the CMDB.

The script works by first retrieving all users from the CMDB, then running through each user and creating creating a CSV string containing some identifying strings and timezone/locale settings, adding all of these strings into a CSV file, and once the file is complete uses the import CSV cmdlet to create a new localisation preference record for each user in the CMDB. Once the new records have been created it clears out any blank localisation records that have been automatically created for the users during the AD import.

In order to run the script you need to have the SMLets module installed and working (http://smlets.codeplex.com/) and it also uses the SMCmdLetSnapin that comes with an SCSM install. The example I am posting here also requires the directory C:\SMAdmin\CSV\ be present on the server, but that can be updated to suit your requirements. There are 3 files involved in this solution: the PowerShell script itself (Set-Locale.ps1), the CSV file that gets updated and imported (Users.csv), and the XML import mapping file that tells SCSM what to do with the CSV file (UserImport.xml).

I’ll start by posting the entire PowerShell script and then go through the salient points.

Import-Module smlets ; 
Add-PSSnapIn SMCmdletSnapIn ;

#Delete any existing data from the CSV
Clear-Content -Path "C:\SMAdmin\CSV\Users.csv"

#retrieve all users from the CMDB and cycle through them
$users = (get-scsmclass Microsoft.AD.User$ | get-scsmobject)
foreach ($user in $users)
    {
        $UserName = $user.UserName               #get the username, used to identify the person
        $Domain = $user.Domain                   #get the domain, this is required for a CSV import
        $ID = $user.ID                           #get the users guid, we will use this to create the ID for the settings object
        $setPref = 'Pref.'                       #used as a prefix for the localsetting object id
        $setID = $setPref,$ID         
        $setID = [string]::join('',$setID)       #combine the users guid and Pref. to make the id for localesettings
        $timezone = 'AUS Eastern Standard Time'  #set timezone
        $locale = '1033'                         #setting the locale, in this case it's English (United States)
        $data = $UserName, $Domain, $setID, $timezone, $locale  #put it all into an array
        [string]::join(',',$data) | ForEach-Object {Add-Content -Value $_ -Path "C:\SMAdmin\CSV\Users.csv"};   #convert the array to a string and append to the csv
     }

#once all users have been added to the csv, import it into the CMDB using the xml mapping file.
Import-SCSMInstance -DataFileName "C:\SMAdmin\CSV\Users.csv" -FormatFileName "C:\SMAdmin\CSV\UserImport.xml"

#Remove any automatically created localization settings, the script has create new ones for everybody
get-scsmclass System.UserPreference.Localization$ | get-scsmobject -filter {DisplayName -notlike "%-%"} | remove-scsmobject -force

Remove-PSSnapIn SMCmdLetSnapIn;
Remove-Module smlets;

In this particular script I am setting all users to the English (United States) language setting, primarily so I don’t need to localise management pack content to get nice enumeration values in notifications. I’m also setting everyone to Australian Eastern Standard time because I’m in Sydney. If, however, I wanted to set the locale and timezone to a different value depending on what country or city a user is in I would be able to do so by retrieving those values for the user from the CMDB and then running through some if statements to set the values accordingly. So where my script currently reads

$timezone = 'AUS Eastern Standard Time'  #set timezone

and I wanted to set timezones for users in different cities, I could change it to read something like

if ($user.City -like "Perth") {$timezone = 'AUS Western Standard Time'}
elseif ($user.City -like "Adelaide") {$timezone = 'AUS Central Standard Time'}
else {$timezone = 'AUS Eastern Standard Time'}

here I have checked to see if the user is in Perth or Adelaide and set their timezones if they are (I made those codes up btw), and if they aren’t in those 2 cities I know they are in Sydney so I default to that timezone.

Alternatively I might be supporting more than one country and so need to set the language for all of my users based on their country, in this case I change the line

$locale = '1033'   #setting the locale

to have some conditional logic that looks at the country and sets the language code (Language Code table here) accordingly

if ($user.Country -like "New Zealand") {$locale = '5129'}
else {$locale = '3081'}

here I’m looking at the users country entry and if it is New Zealand I set the New Zealand Locale ID, and then default to Australia’s (these ones are real codes).

That covers the PowerShell side of things. The other important file is the XML mapping file, if all you are doing is using it for this purpose then you shouldn’t need to change it at all; an important thing to note is that the order of the fields in the CSV and the XML file have to match up so use caution if editing this file. I will post the contents here for reference

<CSVImportFormat>
	<Projection Type="System.User.Projection">
		<Seed>
			<Class Type="System.Domain.User">
				<Property ID="UserName"/>
				<Property ID="Domain"/>
			</Class>
		</Seed>
		<Component Alias="Preference">
			<Seed>
				<Class Type="System.UserPreference.Localization">
					<Property ID="Id"/>
					<Property ID="Timezone"/>
					<Property ID="LocaleID"/>
				</Class>
			</Seed>
		</Component>
	</Projection>
</CSVImportFormat>

So that is basically it, once you have the script running as you want it you can follow Travis’s excellent instructions for scheduling it via a workflow in SCSM.

The solution is available for download here

About BrettHam
I have been working as an ITIL and Service Desk tool consultant for Infinite Technology Solutions based in Sydney, Australia for a number of years now; I have recently started working with Microsoft System Center Service Manager and these are some tricks I have picked up as I go.

6 Responses to Setting Locale and Timezone in Bulk

  1. Rob says:

    Hi Brett – I know this is over 2 yrs since you posted this but I am trying to do this in SCSM 2012 and am getting a few powershell errors.

    I was able to figure out a few things but am stuck with this error message:

    Get-SCClass : Parameter set cannot be resolved using the specified named parame
    ters.
    At C:\Temp\CSV\Set-Locale.ps1:8 char:24
    + $users = (get-scsmclass <<<< Microsoft.AD.User$ | get-scsmobject)
    + CategoryInfo : InvalidArgument: (:) [Get-SCClass], ParameterBin
    dingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.SystemCenter.Cor
    e.Commands.GetSCClassCommand

    Get-SCClass : Parameter set cannot be resolved using the specified named parame
    ters.
    At C:\Temp\CSV\Set-Locale.ps1:27 char:14
    + get-scsmclass <<<< System.UserPreference.Localization$ | get-scsmobject -fil
    ter {DisplayName -notlike "%-%"} | remove-scsmobject -force
    + CategoryInfo : InvalidArgument: (:) [Get-SCClass], ParameterBin
    dingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.SystemCenter.Cor
    e.Commands.GetSCClassCommand

    I am hoping you can assist please. Not very good with powershell yet.

    • BrettHam says:

      Wow, this is a blast from the past. I’m not very involved with SCSM these days but I’ll give it a shot 🙂

      From the looks of things the syntax for Get-SCSMClass has changed a little in 2012 and you might need to specify the parameters when calling it. Try making the following changes:

      Line 8
      FROM: $users = (get-scsmclass Microsoft.AD.User$ | get-scsmobject)
      TO: $users = (get-scsmclass -name Microsoft.AD.User$ | get-scsmobject)

      Line 27
      FROM: get-scsmclass System.UserPreference.Localization$ | get-scsmobject -filter {DisplayName -notlike “%-%”} | remove-scsmobject -force
      TO: get-scsmclass -name System.UserPreference.Localization$ | get-scsmobject -filter {DisplayName -notlike “%-%”} | remove-scsmobject -force

      Hopefully that should sort it out.

      • Rob says:

        BrettHam – Just have to say thank you very much. I was staring at that script for 30 minutes without seeing what was missing. Made the change and updated the csv file to test a user. It worked like a charm. Now I will add everyone in my test enviroment to the csv file and let it run.

        Thanks you very much and have a great day.

      • Rob says:

        Ok got this error
        Unable to commit CSV row (data file: C:\SMAdmin\CSV\Users.csv, line number: 1). Commit failed with the following exception A discovery data item was rejected because the item is already bound to another Membership relationship..

        I’ve seen this before but not sure what it is bound too. It worked for one user but afterwards I cannot make any changes. I will look it up again.

      • BrettHam says:

        It might not allow you to create multiple locale records for a user, try running this line separately and updating the filter to match the user who is causing the issue.

        get-scsmclass -name System.UserPreference.Localization$ | get-scsmobject -filter {DisplayName -notlike “%-%”} | remove-scsmobject -force

  2. Rob says:

    Thanks Brett – I tried to run it using the username which I did first and it still doesn’t like it. I only tried 2 people….I did one first and it worked. Then tried two and am now getting that error everytime….even I go back to run it with one user. I added the username of that first person using the line you wanted me to try but still get the same issue.

    Thanks

Leave a comment