Description

A PowerShell script to configure when users are allowed to logon to the domain in Active Directory. The script reads user Distinguished Names and the allowed hours from a comma delimited file. After adjusting for the local time zone, and converting the values into a byte array, the program assigns the logonHours attribute of the user. The input file is assumed to have the following header line, defining the 8 fields in the file:


DN,Sun,Mon,Tue,Wed,Thu,Fri,Sat


There will follow one line for each user. The Distinguished Names must be enclosed in quotes, because the DN's have embedded commas. Be sure to escape any characters that require it, like a comma in the Common Name, with the backslash escape character, "\". The string value for each day is a sequence of 24 digits, one for each hour in the day. A "0" means the user is not allowed to logon during the hour. A "1" means the user is allowed to logon. The digits can be separated (if desired) by any of the following characters to make them easier to read: a space, "-", ",", "/", "\", "+". Of course, if the hours are separated by commas, then the string for each day must be quoted. An example line, but only showing two of the seven days, follows:


"cn=Jim Smith,ou=West,dc=MyDomain,dc=com",000 000 000 111 111 000 000 000,000 000 011 111 111 111 100 000


In this example, the user is allowed to logon from 9am until 3pm on Sunday (6 hours) and from 7am until 7pm Monday (12 hours).

Script

PowerShell
Edit|Remove
# SetLogonHours.ps1 
# PowerShell program to configure when users are allowed to logon 
# to the domain in Active Directory. 
# Author: Richard Mueller 
# PowerShell Version 1.0 
# August 102011 
# September 182012 - Modify rounding of local time zone bias. 
# April 292013 - Account for negative time zone bias. 
# February 52017 - Fix bug when local time zone bias is 0. 
 
Trap{"Error assigning logonHours to $DN - $_"; Continue;} 
 
# Create an array of 21 bytes, each of 8 bits, representing the 168 hours 
# in a week. 
$LH = New-Object 'Byte[]' 21 
 
# Retrieve local Time Zone bias from machine registry in hours. 
# This bias does not change with Daylight Savings Time. 
$Bias = (Get-ItemProperty ` 
    -Path HKLM:\System\CurrentControlSet\Control\TimeZoneInformation).Bias 
# Account for negative bias. 
If ($Bias -gt 10080){$Bias = $Bias - 4294967296} 
$Bias = [Math]::Round($Bias/600, [MidpointRounding]::AwayFromZero) 
 
# Read user DN's and binary string reprentations of the hours each user is 
# allowed to logon to the domain. There should be one DN and 7 strings of 
# 24 bits each, representing the 7 days (168 hours) in a week, on each line. 
# A "0" means the user is not allowed to logon during the our, a "1" means 
# the user can logon during the hour. The input hours are in local time. 
Import-CSV LogonHours.csv | ForEach { 
    $DN =  $_.DN 
    $Sun = $_.Sun 
    $Mon = $_.Mon 
    $Tue = $_.Tue 
    $Wed = $_.Wed 
    $Thu = $_.Thu 
    $Fri = $_.Fri 
    $Sat = $_.Sat 
 
    # Bind to the user object in AD. 
    $User = [ADSI]"LDAP://$DN" 
    If ($User.sAMAccountName -eq $Null) {"User not found: $DN"} 
    Else 
    { 
        # Append binary strings, one for each day of the week, into one string. 
        $Hours = "$Sun$Mon$Tue$Wed$Thu$Fri$Sat" 
        # Remove any delimiters separating the hours. 
        $Hours = $Hours.Replace(" """) 
        $Hours = $Hours.Replace("-""") 
        $Hours = $Hours.Replace("/""") 
        $Hours = $Hours.Replace("\", "") 
        $Hours = $Hours.Replace(",""") 
        $Hours = $Hours.Replace("+""") 
 
        # Adjust for time zone bias, to convert from local time to UTC. 
        $Len = $Hours.Length 
        If (($Len -ne 168) -or ($Hours -match"[^0-1]")) {"Invalid hours for $DN"} 
        Else 
        { 
            If ($Bias -ge 0) 
            { 
                $Str1 = $Hours.SubString(0, $Len - $Bias) 
                $Str2 = $Hours.SubString($Len - $Bias) 
            } 
            If ($Bias -lt 0) 
            { 
                $Str1 = $Hours.SubString(0, -$Bias) 
                $Str2 = $Hours.SubString(-$Bias) 
            } 
            $Hours = "$Str2$Str1" 
 
            # Populate binary array. 
            For ($k = 0; $k -le 20; $k = $k + 1) 
            { 
                # Convert each 8 hours into one byte. 
                $Value = $Hours.SubString(8*$k, 8) 
                $Hrs = "" 
                # Reverse the hours. 
                For ($j = 0; $j -le 7; $j = $j + 1) 
                { 
                    $Hrs = $Value.SubString($j, 1) + $Hrs 
                } 
                $Byte = [Convert]::ToByte($Hrs, 2) 
                $LH[$k] = $Byte 
            } 
            # Assign the resulting byte array to the logonHours 
            # attribute of the user. 
            $User.logonHours.Value = $LH 
            $User.SetInfo() 
        } 
    } 
}