Editing ActiveDirectory LogonHours with HassAgent

I’m running a Microsoft Active Directory at home (maybe not so many do)
And I have kids that I’ve set up logonhours in Active Directory so their computer log off when it’s time to prepare for bedtime.
That’s straight forward AD management.

But then I thought it would be great to be able to change the hours easily, and it would be great if I would be able to that throught HA. That way I could do it from my phone while I’m not at home and it would be easy for my wife to handle it too.

The solution I found is build with Hass-Agent on the AD Domain Controler. And PowerShell module that I found some help to create from Faris Maleab with some PowerShell script.
And some Input-Numbers Helper in HA and MQTT.

Prerequisit
You need to install Hass-Agent on your Domain Controler, by downloading the installer

You also need to have MQTT setup, and you can read more about that her

And you will need the LogonHours PowerShell Module/function that’s discribed in a later replay.

The PowerShell function

To install:
Create a directory named LogonHours under PowerShell module Directory. (Usualy C:\Program Files\WindowsPowerShell\Modules)
Create a file in that directory named LogonHours.psm1 and copy the text belowe in to that file

#################################################################################################################################################################
#
# PowerShell function for easy editing of LogonHours
#
# Created with help from Faris Maleab instructions at https://www.powershellcenter.com/2021/03/09/manage-ad-user-logon-hours-using-powershell/
#
# To install:
# Create a directory named LogonHours under PowerShell module Directory. (Usualy C:\Program Files\WindowsPowerShell\Modules)
# Create a file that directory named LogonHours.psm1 and copy this text in to that file
#
# Usage:
# Set-LogonHours -Identity User1 -TimeIn24Format @(10,11,12,13,14,15,16,17,18,19,20,21,22,23) -Monday -Friday -NonSelectedDaysare NonWorkingDays
# Where User1 is the username that will be affected, the numbers with in () is the hours you will set, while the weekdays are the days you will apply.
# Read more on Faris web site above...
#
# Version 0.9
# Created by Peter Nordin (pethson) at 2022-08-20
#
################################################################################################################################################################





Function Set-LogonHours{
 [CmdletBinding()]
 Param(
 [Parameter(Mandatory=$True)]
 [ValidateRange(0,23)]
 $TimeIn24Format,
 [Parameter(Mandatory=$True,
 ValueFromPipeline=$True,
 ValueFromPipelineByPropertyName=$True, 
 Position=0)]$Identity,
 [parameter(mandatory=$False)]
 [ValidateSet("WorkingDays", "NonWorkingDays")]$NonSelectedDaysare="NonWorkingDays",
 [parameter(mandatory=$false)][switch]$Sunday,
 [parameter(mandatory=$false)][switch]$Monday,
 [parameter(mandatory=$false)][switch]$Tuesday,
 [parameter(mandatory=$false)][switch]$Wednesday,
 [parameter(mandatory=$false)][switch]$Thursday,
 [parameter(mandatory=$false)][switch]$Friday,
 [parameter(mandatory=$false)][switch]$Saturday
 )
 Process{
 $FullByte=New-Object "byte[]" 21
 $FullDay=[ordered]@{}
 0..23 | foreach{$FullDay.Add($_,"0")}
 $TimeIn24Format.ForEach({$FullDay[$_]=1})
 $Working= -join ($FullDay.values)
 Switch ($PSBoundParameters["NonSelectedDaysare"])
 {
 'NonWorkingDays' {$SundayValue=$MondayValue=$TuesdayValue=$WednesdayValue=$ThursdayValue=$FridayValue=$SaturdayValue="000000000000000000000000"}
 'WorkingDays' {$SundayValue=$MondayValue=$TuesdayValue=$WednesdayValue=$ThursdayValue=$FridayValue=$SaturdayValue="111111111111111111111111"}
 }
 Switch ($PSBoundParameters.Keys)
 {
 'Sunday' {$SundayValue=$Working}
 'Monday' {$MondayValue=$Working}
 'Tuesday' {$TuesdayValue=$Working}
 'Wednesday' {$WednesdayValue=$Working}
 'Thursday' {$ThursdayValue=$Working}
 'Friday' {$FridayValue=$Working}
 'Saturday' {$SaturdayValue=$Working}
 }
 $AllTheWeek="{0}{1}{2}{3}{4}{5}{6}" -f $SundayValue,$MondayValue,$TuesdayValue,$WednesdayValue,$ThursdayValue,$FridayValue,$SaturdayValue
# Timezone Check
 if ((Get-TimeZone).baseutcoffset.hours -lt 0){
 $TimeZoneOffset = $AllTheWeek.Substring(0,168+ ((Get-TimeZone).baseutcoffset.hours))
 $TimeZoneOffset1 = $AllTheWeek.SubString(168 + ((Get-TimeZone).baseutcoffset.hours))
 $FixedTimeZoneOffSet="$TimeZoneOffset1$TimeZoneOffset"
 }
 if ((Get-TimeZone).baseutcoffset.hours -gt 0){
 $TimeZoneOffset = $AllTheWeek.Substring(0,((Get-TimeZone).baseutcoffset.hours))
 $TimeZoneOffset1 = $AllTheWeek.SubString(((Get-TimeZone).baseutcoffset.hours))
 $FixedTimeZoneOffSet="$TimeZoneOffset1$TimeZoneOffset"
 }
 if ((Get-TimeZone).baseutcoffset.hours -eq 0){
 $FixedTimeZoneOffSet=$AllTheWeek
 }
 $i=0
 $BinaryResult=$FixedTimeZoneOffSet -split '(\d{8})' | Where {$_ -match '(\d{8})'}
 Foreach($singleByte in $BinaryResult){
 $Tempvar=$singleByte.tochararray()
 [array]::Reverse($Tempvar)
 $Tempvar= -join $Tempvar
 $Byte = [Convert]::ToByte($Tempvar, 2)
 $FullByte[$i]=$Byte
 $i++
 }
 Set-ADUser  -Identity $Identity -Replace @{logonhours = $FullByte}                                   
 }
 end{
 Write-Output "All Done :)"
 }
 }

The PowerShell script needed is simple and takes two arguments StartHour and StopHour.
The Script extracts the hours between StartHour and StopHour to be inserted in the set-logonhours function used to set the Logon Hours in Active Directory.
The username is hardcoded in this example PowerShell script as User1
The example script sets the logonhour for to day by using (get-date).DayOfWeek

########################################################################################################################
#
# PowerShell script to set LogonHours for a specific user from HomeAssistant through MQTT and HassAgent
#
# PowerShell script location is preferably c:\LogonHours
# PowerShell script name is preferably LogonHours.ps1
#
# The scripts needs two arguments, StartHour and StopHour
# c:\LogonHours\LogonHours.ps1 8 17
# Where 8 is the StartHour and 17 is the StopHour
# The script creats a temporary PowerShell script named temp.ps1 that's executed att the end
# The username is hardcoded in this example as User1
#
# Version 0.9
# Created by Peter Nordin (pethson) 2023-09-28
#
########################################################################################################################





$dag=(get-date).DayOfWeek
For ($i = $args[0]; $i -lt $args[1];$i++ )
{
#Write-Host $i
$tid=$tid+","+$i
}

'Set-LogonHours -Identity User1 -TimeIn24Format @('+$tid.trimstart(",")+') -'+$dag+' -NonSelectedDaysare NonWorkingDays' | Out-File -FilePath c:\LogonHours\temp.ps1
invoke-expression -Command c:\LogonHours\temp.ps1

I used HA Helpers for input to the script.
Specificly I used two Number Helper


Where I used the settings for min 0 and max 24 as LogonHours in Active Directory uses 24 hour format.
And I named it LogonHour Start and LogonHours Stop

We will use these in hour dashboard and automation later on.
If you prefere you can use Input field insted of sliders.

Next step is to creat an Entities card in one of your dashboard.
My card setup looks like this


And here I’m able to change the start and stop hour for the logon.
The final result looks like this
image

It’s time to set up the command in HassAgent on your Domain Controller
Open HassAgent (you can find in in your toolbar in your lower left corner)
Klick on Commands


And then klick on Add New

Select PowerShell and change the name to something thats easy to rember. My DC servername is v-AD16 and use that as the start of the name then I wrote LogonHours
I left the Command or Script empty, as I will use MQTT Action to trigger this command, and I will send the filename, location and arguments in the MQTT.publish payload later.

As you see down to the right we can look at and copy the MQTT Action Topic that we will need later in our automation. Click it and copy the topic (you can open this and copy it later too)

Then click Stor Command and then click Store and activate the command

Now that the HassAgent Command is set up we head back to HA and create the automation

First I use to validate the template I will use in my automation. And for this scenario I would like to extract the two values for input_number.logonhours_start and input_number.logonhours_stop. I do not want any decimals so I will try

{{ states('input_number.logonhours_start') | round}} {{ states('input_number.logonhours_stop') | round}}

And if I go to validate the template it looks like this

Right now my starttime is set to 0800 and my stoptime is set to 1700 (you can only set hours in Active Directory LogonHours, that’s why ignored the minutes)

Head over to Settings and then Automations and Scenes
Create a new automation
image

And add a trigger for state
Select the entities for both LogonHours Start and LogonHours Stop

Next we create an action for call service and select MQTT: Publish
And here we need that MQTT Action Topic we looked at when we created the HassAgent Command

Past in the MQTT Action Topic from HassAgent Command in to Topic
Select Payload and the we past in the path to the PowerShell script on you Domain Controller and the template we tested earlier
Would look something like

" \"c:\\LogonHours\\LogonHours.ps1 {{ states('input_number.logonhours_start') | round}} {{ states('input_number.logonhours_stop') | round}} \""

Note that we need to use Escaped character for " and backslash, thats why the path to PowerShell script looks a bit odd.

When you past in the payload the webpage change to YAML Edit mode as it can not handle this in GUI

Save your automation and you should be all done.
If you now move the sliders for LogonHours in your dashboard your automation should change the LogonOurs for User1 in you Active Directory. Be aware that you will have to refresh the Active Directory Users and Computers GUI to see the changes.

You could extend this to use a dynamic user or set the LogonHours for a couple of days instead of only for today.

As this are for my kids I have an automation that runs every day in the middel of the night to set the todays value for LogonHours Start and LogonHours Stop. Depending if it’s school day or not the values are different. This way theire LogonHours in ActiveDirectory is only set for today, and I will rename my kids to User1 and User2 :wink:

1 Like