.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
| _ ____ _ _ _ |
| / \ | _ \ | | ___ __ _ ___ _ __ | | | | ___ _ _ _ __ ___ |
| / _ \ | | | |_____| | / _ \ / _` |/ _ \| '_ \| |_| |/ _ \| | | | '__/ __| |
| / ___ \| |_| |_____| |__| (_) | (_| | (_) | | | | _ | (_) | |_| | | \__ \ |
| /_/ \_\____/ |_____\___/ \__, |\___/|_| |_|_| |_|\___/ \__,_|_| |___/ |
| |___/ |
.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
| PowerShell Scripts para Gerenciamento de Horarios de Logon no Active Directory |
.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
- Sobre o Projeto
- Como Funciona o logonHours no AD
- Arquitetura da Solucao
- Pre-requisitos
- Instalacao
- Uso
- Fluxogramas
- Exemplos Praticos
- Troubleshooting
- FAQ
- Changelog
- Licenca
Este projeto fornece scripts PowerShell para controlar os horarios em que usuarios podem fazer logon no dominio Active Directory.
| Cenario | Solucao |
|---|---|
| Bloquear logon fora do horario comercial | Set-ADLogonHours com range 08:00-18:00 |
| Bloquear logon noturno (compliance) | Set-ADLogonHours com range 03:00-23:00 |
| Remover todas as restricoes | Clear-ADLogonHours modo CLEAR |
| Desfazer alteracao indevida | Clear-ADLogonHours modo RESTORE |
| Script | Funcao |
|---|---|
Set-ADLogonHours.ps1 |
Aplica restricao de horario em lista de usuarios |
Clear-ADLogonHours.ps1 |
Remove ou restaura restricoes de horario |
O Active Directory armazena restricoes de horario de logon no atributo logonHours, que e um byte array de 21 bytes.
21 bytes = 168 bits = 7 dias x 24 horas
Cada bit representa 1 hora da semana:
1= Usuario pode fazer logon naquela hora0= Usuario esta bloqueado naquela hora
| Dia | Bytes | Indices | Horas Cobertas |
|---|---|---|---|
| Domingo | 3 | [0][1][2] |
00:00 - 23:59 |
| Segunda | 3 | [3][4][5] |
00:00 - 23:59 |
| Terca | 3 | [6][7][8] |
00:00 - 23:59 |
| Quarta | 3 | [9][10][11] |
00:00 - 23:59 |
| Quinta | 3 | [12][13][14] |
00:00 - 23:59 |
| Sexta | 3 | [15][16][17] |
00:00 - 23:59 |
| Sabado | 3 | [18][19][20] |
00:00 - 23:59 |
| Total | 21 | [0-20] |
168 horas |
Visualizacao Linear:
[0][1][2] [3][4][5] [6][7][8] [9][10][11] [12][13][14] [15][16][17] [18][19][20]
|_______| |_______| |_______| |_________| |__________| |__________| |__________|
Dom Seg Ter Qua Qui Sex Sab
Cada dia tem 3 bytes (24 horas / 8 bits por byte = 3 bytes):
Byte 0 (Domingo 00:00-07:59):
βββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ
β 0 β 1 β 2 β 3 β 4 β 5 β 6 β 7 β β Horas (UTC)
βββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ
Byte 1 (Domingo 08:00-15:59):
βββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ
β 8 β 9 β10 β11 β12 β13 β14 β15 β β Horas (UTC)
βββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ
Byte 2 (Domingo 16:00-23:59):
βββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ
β16 β17 β18 β19 β20 β21 β22 β23 β β Horas (UTC)
βββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ
IMPORTANTE: O Active Directory armazena
logonHoursem UTC.
O script converte automaticamente o horario local (BRT = UTC-3) para UTC:
Exemplo: Bloquear 23:00-03:00 BRT
BRT 23:00 β UTC 02:00 (dia seguinte)
BRT 03:00 β UTC 06:00
Portanto, no AD sera armazenado: bloqueio UTC 02:00-06:00
| Situacao | Comportamento |
|---|---|
| Novo logon | BLOQUEADO - Usuario recebe erro "Account restrictions..." |
| Sessao ativa | NAO DESCONECTA automaticamente |
| Forcar logoff | Habilitar GPO "Network Security: Force logoff when logon hours expire" |
flowchart TB
subgraph INPUT["π₯ ENTRADA"]
TXT["π usuarios.txt<br/>Lista de matriculas"]
CSV["π backup.csv<br/>Backup anterior"]
end
subgraph SCRIPTS["βοΈ SCRIPTS"]
SET["Set-ADLogonHours.ps1"]
CLEAR["Clear-ADLogonHours.ps1"]
end
subgraph AD["π’ ACTIVE DIRECTORY"]
ATTR["logonHours<br/>(21 bytes)"]
end
subgraph OUTPUT["π€ SAIDA"]
LOG["π LogonHours_*.log<br/>Log de execucao"]
BACKUP["π¦ LogonHours_Backup_*.csv<br/>Backup automatico"]
end
TXT --> SET
SET --> |"Grava restricao"| ATTR
SET --> LOG
SET --> BACKUP
TXT --> CLEAR
CSV --> CLEAR
CLEAR --> |"Remove/Restaura"| ATTR
CLEAR --> LOG
| Requisito | Detalhes |
|---|---|
| Windows | Server 2012 R2+ ou Windows 10/11 |
| PowerShell | 5.1 ou superior |
| Privilegios | Executar como Administrador |
O script verifica e oferece instalacao automatica:
# Windows Server
Install-WindowsFeature RSAT-AD-PowerShell
# Windows 10/11
Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0O usuario que executa o script precisa de permissao para modificar o atributo logonHours dos usuarios-alvo.
Geralmente requer:
- Membership no grupo Account Operators, ou
- Delegacao especifica de "Write logonHours"
git clone https://github.com/lyonzin/AD-LogonHours.git
cd AD-LogonHoursAD-LogonHours/
βββ Set-ADLogonHours.ps1 # Script para aplicar restricoes
βββ Clear-ADLogonHours.ps1 # Script para remover/restaurar
βββ README.md # Esta documentacao
βββ LICENSE # Licenca MIT
βββ examples/
βββ usuarios_exemplo.txt # Exemplo de lista de usuarios
Crie um arquivo .txt com um username por linha:
# Comentarios sao ignorados (linhas iniciando com #)
joao.silva
maria.santos
pedro.costa
Ou use um .csv com coluna SamAccountName:
SamAccountName,DisplayName
joao.silva,Joao da Silva
maria.santos,Maria SantosEdite o inicio do script para ajustar:
$UserList = "C:\Scripts\usuarios.txt" # Lista de usuarios
$HorarioPermitidoDe = 3 # Pode logar A PARTIR de (BRT)
$HorarioPermitidoAte = 23 # NAO pode mais A PARTIR de (BRT)
$DiasAplicados = @('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')
$UTCOffset = -3 # BRT = UTC-3# Modo normal
.\Set-ADLogonHours.ps1
# Modo WhatIf (simula sem aplicar)
.\Set-ADLogonHours.ps1 -WhatIfflowchart TD
START([π Inicio]) --> PREREQ{Pre-requisitos OK?}
PREREQ -->|β Falhou| EXIT1([β Encerra com erro])
PREREQ -->|β
OK| READ[π Le lista de usuarios]
READ --> BUILD[π§ Constroi byte array<br/>21 bytes = 168 horas]
BUILD --> CONVERT[π Converte BRT β UTC]
CONVERT --> LOOP{Proximo usuario?}
LOOP -->|Sim| GETUSER[π€ Busca usuario no AD]
GETUSER --> BACKUP[πΎ Backup do valor atual]
BACKUP --> HASATTR{Ja tem logonHours?}
HASATTR -->|Sim| CLEAR[ποΈ Clear atributo]
HASATTR -->|Nao| ADD
CLEAR --> ADD[β Add novo valor]
ADD --> LOG[π Registra no log]
LOG --> LOOP
LOOP -->|Nao| SAVEBACKUP[πΎ Salva backup CSV]
SAVEBACKUP --> SUMMARY[π Exibe resumo]
SUMMARY --> END([β
Fim])
style START fill:#4caf50,color:#fff
style END fill:#4caf50,color:#fff
style EXIT1 fill:#f44336,color:#fff
| Modo | Descricao | Arquivo Necessario |
|---|---|---|
CLEAR |
Remove restricao (libera 24/7) | Lista de usuarios (.txt/.csv) |
RESTORE |
Restaura valor original do backup | Backup CSV gerado pelo Set |
$Modo = "CLEAR" # "CLEAR" ou "RESTORE"
$ArquivoUsuarios = "C:\Scripts\usuarios.txt" # Usado no modo CLEAR
$ArquivoBackup = "" # Usado no modo RESTORE# Modo CLEAR - Remove todas as restricoes
.\Clear-ADLogonHours.ps1
# Modo RESTORE - Restaura valores originais
# (edite $Modo = "RESTORE" e $ArquivoBackup = "caminho\backup.csv")
.\Clear-ADLogonHours.ps1flowchart TD
START([π Inicio]) --> PREREQ{Pre-requisitos OK?}
PREREQ -->|β Falhou| EXIT1([β Encerra com erro])
PREREQ -->|β
OK| MODE{Qual modo?}
MODE -->|CLEAR| READTXT[π Le lista de usuarios]
MODE -->|RESTORE| READCSV[π Le backup CSV]
READTXT --> LOOPCLR{Proximo usuario?}
LOOPCLR -->|Sim| GETCLR[π€ Busca usuario no AD]
GETCLR --> HASCLR{Tem logonHours?}
HASCLR -->|Nao| SKIPCLR[βοΈ Skip - ja liberado]
HASCLR -->|Sim| DOCLR[ποΈ Clear logonHours]
SKIPCLR --> LOGCLR[π Log]
DOCLR --> LOGCLR
LOGCLR --> LOOPCLR
LOOPCLR -->|Nao| SUMMARY
READCSV --> LOOPRST{Proximo registro?}
LOOPRST -->|Sim| GETRST[π€ Busca usuario no AD]
GETRST --> ORIGINAL{Original era NULL?}
ORIGINAL -->|Sim| DORST_NULL[ποΈ Clear logonHours]
ORIGINAL -->|Nao| DORST_RESTORE[β»οΈ Restore byte array]
DORST_NULL --> LOGRST[π Log]
DORST_RESTORE --> LOGRST
LOGRST --> LOOPRST
LOOPRST -->|Nao| SUMMARY
SUMMARY[π Exibe resumo]
SUMMARY --> END([β
Fim])
style START fill:#4caf50,color:#fff
style END fill:#4caf50,color:#fff
style EXIT1 fill:#f44336,color:#fff
flowchart LR
subgraph FASE1["π FASE 1: Preparacao"]
A1[Criar lista<br/>de usuarios] --> A2[Definir horarios<br/>permitidos]
end
subgraph FASE2["βοΈ FASE 2: Aplicacao"]
B1[Executar<br/>Set-ADLogonHours]
B1 --> B2[Backup automatico<br/>gerado]
end
subgraph FASE3["π FASE 3: Rollback"]
C1{Precisa desfazer?}
C1 -->|Liberar 24/7| C2[Clear modo CLEAR]
C1 -->|Restaurar original| C3[Clear modo RESTORE]
end
A2 --> B1
B2 --> C1
flowchart TD
subgraph INPUT["Entrada (BRT)"]
H1["HorarioPermitidoDe = 3<br/>HorarioPermitidoAte = 23"]
end
subgraph CALC["Calculo"]
C1["Range permitido BRT:<br/>03:00 - 22:59"]
C2["Aplicar offset -3h"]
C3["Range UTC:<br/>06:00 - 01:59 (dia seguinte)"]
end
subgraph OUTPUT["Byte Array"]
O1["Bits ligados = horas permitidas<br/>Bits desligados = horas bloqueadas"]
end
H1 --> C1 --> C2 --> C3 --> O1
flowchart TD
CHECK{Start >= End?}
CHECK -->|Nao| DIURNO["π
Range DIURNO<br/>Ex: 08:00-18:00<br/>Horas: 08,09,10...17"]
CHECK -->|Sim| NOTURNO["π Range NOTURNO<br/>Ex: 23:00-03:00<br/>Horas: 23,00,01,02"]
NOTURNO --> SPILLOVER["β οΈ Horas apos meia-noite<br/>caem no DIA SEGUINTE"]
# Configuracao no Set-ADLogonHours.ps1
$HorarioPermitidoDe = 3 # Pode logar a partir das 03:00
$HorarioPermitidoAte = 23 # Bloqueado a partir das 23:00
# Resultado: Usuario pode logar entre 03:00 e 22:59$HorarioPermitidoDe = 8 # Pode logar a partir das 08:00
$HorarioPermitidoAte = 18 # Bloqueado a partir das 18:00
# Resultado: Usuario pode logar entre 08:00 e 17:59$DiasAplicados = @('Monday','Tuesday','Wednesday','Thursday','Friday')
# Resultado: Sabado e Domingo completamente bloqueados# 1. O Set-ADLogonHours.ps1 gerou: LogonHours_Backup_20260127_143022.csv
# 2. Edite Clear-ADLogonHours.ps1:
$Modo = "RESTORE"
$ArquivoBackup = ".\LogonHours_Backup_20260127_143022.csv"
# 3. Execute:
.\Clear-ADLogonHours.ps1| Causa | Solucao |
|---|---|
| Username incorreto | Verifique o SamAccountName correto no AD |
| Usuario em outro dominio | Use o formato DOMINIO\usuario ou FQDN |
| Typo no arquivo | Verifique espacos extras ou caracteres invisΓveis |
# Verificar conectividade
Test-ComputerSecureChannel -Verbose
# Verificar DNS
nslookup _ldap._tcp.dc._msdcs.seudominio.com
# Verificar se esta no dominio
(Get-WmiObject Win32_ComputerSystem).DomainO usuario executando precisa de:
- Permissao de escrita no atributo
logonHours - Geralmente: membership em Account Operators ou Domain Admins
| Sintoma | Causa | Solucao |
|---|---|---|
| Usuario continua logando | Sessao ja estava ativa | Forcar logoff via GPO |
| Horario errado | Fuso horario incorreto | Verificar $UTCOffset no script |
| Funcionou parcialmente | Range noturno mal configurado | Verificar se Start > End |
# Ver valor raw
Get-ADUser -Identity "joao.silva" -Properties logonHours | Select logonHours
# Ver de forma legivel
$user = Get-ADUser -Identity "joao.silva" -Properties logonHours
[System.BitConverter]::ToString($user.logonHours)Nao. O atributo logonHours apenas impede novos logons. Para desconectar automaticamente quando o horario expira, habilite a GPO:
Computer Configuration β Policies β Windows Settings β Security Settings β Local Policies β Security Options
β "Network Security: Force logoff when logon hours expire" = Enabled
Nao. O AD trabalha com granularidade de 1 hora. Se precisar de controle mais fino, considere:
- GPOs com scripts de logon/logoff
- Solucoes de terceiros (PAM, Identity Manager)
Nao diretamente. Este script e para Active Directory on-premises. Para Azure AD/Entra ID, use:
- Conditional Access Policies
- Azure AD Identity Governance
Atualmente o script trabalha com lista de usuarios. Para aplicar em OUs, faca:
# Exportar usuarios de uma OU para arquivo
Get-ADUser -SearchBase "OU=Vendas,DC=empresa,DC=com" -Filter * |
Select-Object -ExpandProperty SamAccountName |
Out-File -FilePath "usuarios_vendas.txt"O backup CSV armazena o logonHours original em Base64. Nao contem senhas ou dados sensiveis - apenas o byte array de restricao de horario.
- Release inicial
Set-ADLogonHours.ps1: Aplicacao de restricoesClear-ADLogonHours.ps1: Remocao/restauracao de restricoes- Backup automatico em CSV
- Suporte a range noturno (cruza meia-noite)
- Conversao automatica BRT β UTC
- Verificacao de pre-requisitos com instalacao automatica do modulo AD
Este projeto esta licenciado sob a MIT License.
Ailton Rocha (Lyon.)
- GitHub: @lyonzin