ajustes
This commit is contained in:
parent
48b2c46063
commit
5ccc8653f6
|
|
@ -0,0 +1,56 @@
|
||||||
|
# ===================================================================
|
||||||
|
# Arquivo .gitignore para Projeto de Automação PowerShell
|
||||||
|
# Data: 15 de outubro de 2025
|
||||||
|
# Contexto: Ignora arquivos temporários, logs, módulos e configurações locais.
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# --- Arquivos de Log e Saída ---
|
||||||
|
# Ignora logs gerados pelos scripts e relatórios de testes
|
||||||
|
*.log
|
||||||
|
*.log.*
|
||||||
|
TestResults/
|
||||||
|
*.trx
|
||||||
|
*.xml
|
||||||
|
output/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# --- Configurações de Editores ---
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/settings.json
|
||||||
|
|
||||||
|
# --- Dependências e Módulos do PowerShell ---
|
||||||
|
# Ignora a pasta de módulos que pode ser instalada localmente
|
||||||
|
/modules/
|
||||||
|
/PSDepend/
|
||||||
|
/Pester/
|
||||||
|
|
||||||
|
# --- Arquivos do Sistema Operacional ---
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# --- Arquivos Sensíveis ---
|
||||||
|
# NUNCA comite credenciais, chaves ou arquivos de ambiente
|
||||||
|
*.env
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
credentials.xml
|
||||||
|
*cred*
|
||||||
|
*secret*
|
||||||
|
|
||||||
|
# --- Outros ---
|
||||||
|
# Arquivos de histórico do PowerShell
|
||||||
|
*History.txt
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// Use o IntelliSense para saber mais sobre os atributos possíveis.
|
||||||
|
// Focalizar para exibir as descrições dos atributos existentes.
|
||||||
|
// Para obter mais informações, acesse: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "PowerShell: Launch Current File",
|
||||||
|
"type": "PowerShell",
|
||||||
|
"request": "launch",
|
||||||
|
"script": "${file}",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
175
README.md
175
README.md
|
|
@ -1,3 +1,5 @@
|
||||||
|
Plaintext
|
||||||
|
|
||||||
# Plataforma Unificada de Trabalho Digital
|
# Plataforma Unificada de Trabalho Digital
|
||||||
|
|
||||||

|

|
||||||
|
|
@ -24,121 +26,132 @@ Conjunto de scripts e aplicações para automação de tarefas de gestão de ide
|
||||||
|
|
||||||
## 🌟 Visão Geral do Projeto
|
## 🌟 Visão Geral do Projeto
|
||||||
|
|
||||||
Este projeto detalha a criação de uma **Plataforma Unificada de Trabalho Digital**, um investimento estratégico projetado para **reduzir custos operacionais**, **aumentar a segurança e a conformidade (LGPD)** e **melhorar a agilidade organizacional**.
|
Este projeto detalha a criação de uma **Plataforma Unificada de Trabalho Digital**, um portal de autoatendimento (self-service) e automação projetado para modernizar e otimizar a gestão de identidades e acessos. A plataforma visa reduzir a carga de trabalho do time de TI, empoderar os usuários, aumentar a segurança e garantir a conformidade com a LGPD através de processos automatizados e auditáveis.
|
||||||
|
|
||||||
O objetivo é transformar processos manuais de gestão de identidades (criação de usuários, resets de senha, gestão de grupos, desligamentos) em um fluxo de trabalho automatizado, seguro e eficiente, orquestrado por um portal de autoatendimento para colaboradores, gestores e RH.
|
|
||||||
|
|
||||||
Os principais objetivos incluem:
|
|
||||||
- **Automatizar 80% das solicitações de TI rotineiras**.
|
|
||||||
- **Unificar o acesso a sistemas-chave** como Exchange, Nextcloud e Zammad.
|
|
||||||
- **Mitigar riscos de segurança** através da automação de processos de desligamento e higienização de contas inativas.
|
|
||||||
- **Empoderar colaboradores e gestores** com ferramentas de autoatendimento.
|
|
||||||
|
|
||||||
Para um guia completo sobre a arquitetura e o funcionamento de cada script, consulte a **[Wiki do Projeto](link-para-a-wiki-do-seu-gitea)**.
|
|
||||||
|
|
||||||
## 🚀 Módulos e Funcionalidades Principais
|
## 🚀 Módulos e Funcionalidades Principais
|
||||||
|
|
||||||
A plataforma é dividida nos seguintes módulos, conforme o dossiê do projeto:
|
1. **Portal de Autoatendimento do Usuário:**
|
||||||
|
* Login seguro (integração com AD).
|
||||||
|
* Atualização de dados cadastrais (telefone, endereço, etc.).
|
||||||
|
* Reset de senha de forma autônoma e segura.
|
||||||
|
* Visualização de organograma.
|
||||||
|
|
||||||
#### Módulo 1: Portal do Colaborador (Hub Digital)
|
2. **Portal de Gestão (RH e Gestores):**
|
||||||
- Autenticação Segura via LDAPS
|
* Formulário inteligente para requisição de novos colaboradores.
|
||||||
- Dashboard Unificado com Widgets (AD, Exchange, Nextcloud, Zammad)
|
* Fluxo de aprovação para criação de contas.
|
||||||
- Alteração de Senha (Self-Service)
|
* Processo automatizado de desligamento (offboarding).
|
||||||
- Atualização de Foto de Perfil
|
* Gestão de grupos de acesso e distribuição.
|
||||||
|
|
||||||
#### Módulo 2: Portal do Gestor
|
3. **Automação e Orquestração (Backend):**
|
||||||
- Criação de Novos Colaboradores a partir de templates
|
* Scripts PowerShell robustos para provisionamento em Active Directory e Exchange.
|
||||||
- Criação e Gestão de Grupos de Distribuição
|
* Higienização automática de dados do AD (contas inativas, dados incompletos).
|
||||||
|
* Módulo de auditoria e geração de relatórios de conformidade.
|
||||||
|
|
||||||
#### Módulo 3: Gestão de Colaboradores (RH)
|
4. **Integrações e Segurança:**
|
||||||
- Painel de Consulta e Edição de Colaboradores Ativos
|
* Integração Multi-AD (múltiplas florestas/domínios).
|
||||||
- Processo de Desligamento Totalmente Automatizado
|
* Conexão com sistemas de SIEM para monitoramento de eventos.
|
||||||
|
* Painel de gestão para administradores de TI.
|
||||||
#### Módulo 4: Automação e Governança (Backend)
|
|
||||||
- Higienização Agendada de Contas de Usuários e Computadores Inativos
|
|
||||||
- Mapeamento e Atualização Automática do Organograma no AD
|
|
||||||
|
|
||||||
#### Módulo 5: Painel de Auditoria e Relatórios
|
|
||||||
- Geração de Relatórios de Atividade de Login
|
|
||||||
- Exportação Segura de Logs de Auditoria da Plataforma
|
|
||||||
|
|
||||||
## 🛠️ Tecnologias Utilizadas
|
## 🛠️ Tecnologias Utilizadas
|
||||||
|
|
||||||
- **Backend (Automação):** PowerShell 5.1+
|
* **Backend/Automação:** PowerShell 5.1+
|
||||||
- **Sistemas Alvo:** Active Directory, Microsoft Exchange
|
* **Framework de Testes:** Pester
|
||||||
- **Backend (API):** *(A definir, ex: Node.js, Python/Flask, C#/.NET)*
|
* **Controle de Versão:** Git / Gitea
|
||||||
- **Frontend:** *(A definir, ex: React, Vue.js, Angular)*
|
* **Frontend (Portal):** HTML5, CSS3, JavaScript (Vue.js ou React a ser definido)
|
||||||
- **Banco de Dados (Configurações):** *(A definir, ex: PostgreSQL, MariaDB)*
|
* **Backend (Portal):** Node.js ou .NET Core (a ser definido)
|
||||||
|
* **Banco de Dados:** PostgreSQL ou MariaDB (a ser definido)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🏁 Começando (Getting Started)
|
## 🏁 Começando (Getting Started)
|
||||||
|
|
||||||
Para configurar um ambiente de desenvolvimento local, siga os passos abaixo.
|
Esta seção descreve como configurar o ambiente para executar os scripts de automação.
|
||||||
|
|
||||||
### Pré-requisitos
|
### Pré-requisitos
|
||||||
|
|
||||||
- PowerShell 5.1 ou superior
|
Para executar os scripts de automação e os testes contidos neste repositório, o ambiente precisa atender aos seguintes requisitos:
|
||||||
- Módulo `ActiveDirectory` (instalado via RSAT - Ferramentas de Administração de Servidor Remoto)
|
|
||||||
- Ferramentas de Gerenciamento do Exchange
|
1. **PowerShell 5.1** ou superior.
|
||||||
- Acesso a um ambiente de Active Directory e Exchange para testes
|
2. Módulo **ActiveDirectory** para PowerShell:
|
||||||
- *(Outros pré-requisitos para a stack web, como Node.js, Python, etc.)*
|
* Pode ser instalado via "Recursos Opcionais" do Windows ou com o comando:
|
||||||
|
```powershell
|
||||||
|
Install-WindowsFeature RSAT-AD-PowerShell
|
||||||
|
```
|
||||||
|
3. Módulo **Pester** para execução dos testes:
|
||||||
|
* Normalmente já vem instalado nas versões mais recentes do Windows. Para instalar ou atualizar, use:
|
||||||
|
```powershell
|
||||||
|
Install-Module -Name Pester -Force -SkipPublisherCheck
|
||||||
|
```
|
||||||
|
|
||||||
### Instalação
|
### Instalação
|
||||||
|
|
||||||
1. **Clone o repositório:**
|
1. Clone o repositório do Gitea para sua máquina local:
|
||||||
```sh
|
```bash
|
||||||
git clone [URL_DO_SEU_REPOSITORIO_GITEA]
|
git clone [http://10.10.253.128/seu-usuario/plataforma-unificada-ad.git](http://10.10.253.128/seu-usuario/plataforma-unificada-ad.git)
|
||||||
cd portal-gestao-automation
|
|
||||||
```
|
```
|
||||||
2. **Configuração do Backend:**
|
2. Navegue até o diretório do projeto:
|
||||||
*(Instruções para configurar a API que irá executar os scripts)*
|
```bash
|
||||||
3. **Configuração do Frontend:**
|
cd plataforma-unificada-ad
|
||||||
*(Instruções para instalar dependências e iniciar o servidor de desenvolvimento do frontend)*
|
```
|
||||||
4. **Configuração dos Scripts:**
|
3. Execute os testes para garantir que tudo está funcionando como esperado:
|
||||||
- Nenhuma instalação é necessária para os scripts, mas garanta que a política de execução do PowerShell permita a execução de scripts locais:
|
|
||||||
```powershell
|
```powershell
|
||||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
Invoke-Pester -Path .\tests\
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📖 Como Usar
|
## 📖 Como Usar
|
||||||
|
|
||||||
Os scripts de automação estão localizados na pasta `/scripts` e são projetados para serem chamados pela API do backend. Para testes manuais, podem ser executados diretamente via PowerShell.
|
### Exemplo de Execução do Script
|
||||||
|
|
||||||
|
O script `cria-usuario-unico.ps1` é o coração do provisionamento individual. Ele deve ser executado a partir de um terminal PowerShell com os parâmetros necessários.
|
||||||
|
|
||||||
|
**Exemplo de uso:**
|
||||||
|
|
||||||
**Exemplo (Criação de Usuário em Massa):**
|
|
||||||
```powershell
|
```powershell
|
||||||
# 1. Prepare as credenciais de forma segura
|
# 1. Primeiro, armazene as credenciais de forma segura em variáveis
|
||||||
$clientCred = Get-Credential
|
$clientCred = Get-Credential
|
||||||
$exchangeCred = Get-Credential
|
$exchangeCred = Get-Credential
|
||||||
|
|
||||||
# 2. Execute o script apontando para o arquivo CSV
|
# 2. Execute o script localizado na pasta 'src' com os parâmetros do usuário
|
||||||
.\scripts\identity\New-ADUserFromTemplate.ps1 -CsvPath "C:\temp\novos_usuarios.csv" -ClientADServer "dc01.local" -ClientADCredential $clientCred -ExchangeADServer "mail.local" -ExchangeADCredential $exchangeCred
|
.\src\cria-usuario-unico.ps1 `
|
||||||
```
|
-ClientADServer "dc01.cliente.local" `
|
||||||
> Para detalhes completos sobre os parâmetros de cada script e a estrutura dos arquivos de entrada, consulte a **Wiki do Projeto**.
|
-ClientADCredential $clientCred `
|
||||||
|
-ExchangeADServer "dc01.exchange.local" `
|
||||||
|
-ExchangeADCredential $exchangeCred `
|
||||||
|
-SamAccountName "joao.novo" `
|
||||||
|
-UserPrincipalName "joao.novo@cliente.com.br" `
|
||||||
|
-GivenName "Joao" `
|
||||||
|
-Surname "Novo" `
|
||||||
|
-Name "Joao Novo" `
|
||||||
|
-Path "OU=Usuarios,DC=cliente,DC=local"
|
||||||
|
🧪 Executando os Testes
|
||||||
|
Os testes automatizados garantem a qualidade e a estabilidade do código. Eles foram criados com o framework Pester. Para executá-los, navegue até a pasta raiz do projeto e use o seguinte comando:
|
||||||
|
|
||||||
## 🗺️ Fases do Projeto (Roadmap)
|
PowerShell
|
||||||
|
|
||||||
A implementação seguirá as fases estratégicas definidas no dossiê do projeto para mitigar riscos e entregar valor de forma contínua.
|
Invoke-Pester -Path .\tests\
|
||||||
|
🗺️ Fases do Projeto (Roadmap)
|
||||||
|
Fase Entrega Principal Estimativa Entregáveis e Valor Agregado
|
||||||
|
1 Discovery e Arquitetura 3-4 Semanas Blueprint técnico, DPIA/LGPD, planejamento de extensão AD
|
||||||
|
2 Prova de Conceito (PoC) de Riscos 2 Semanas Validação de integrações complexas (Multi-AD, SIEM)
|
||||||
|
3 MVP - Autoatendimento Essencial 6-8 Semanas Portal com login, troca de senha e atualização de perfil
|
||||||
|
4 Módulo de Gestão (Gestores e RH) 5-6 Semanas Criação de usuários, grupos, e módulo de desligamento
|
||||||
|
5 Módulos de Automação e Auditoria 5-7 Semanas Scripts de higienização, organograma e painel de auditoria
|
||||||
|
6 Integração com Sistemas Externos 4-6 Semanas Conectores (Exchange, Nextcloud, Zammad), dashboard unificado
|
||||||
|
7 Painel de Gestão e Configurações 3-4 Semanas Painel de admin para gerenciar variáveis e integrações
|
||||||
|
8 Lançamento e Melhoria Contínua Contínuo Plataforma em produção, monitoramento de KPIs e treinamento
|
||||||
|
|
||||||
| Fase | Título | Duração Estimada | Entregáveis Chave |
|
Exportar para as Planilhas
|
||||||
| :--: | ------------------------------------------ | :--------------: | --------------------------------------------------------- |
|
🤝 Como Contribuir
|
||||||
| 1 | Análise e Arquitetura Estratégica | 3-4 Semanas | Blueprint técnico, DPIA/LGPD, planejamento de extensão AD |
|
Contribuições são o que tornam a comunidade um lugar incrível para aprender, inspirar e criar. Qualquer contribuição que você fizer será muito apreciada.
|
||||||
| 2 | Prova de Conceito (PoC) de Riscos | 2 Semanas | Validação de integrações complexas (Multi-AD, SIEM) |
|
|
||||||
| 3 | MVP - Autoatendimento Essencial | 6-8 Semanas | Portal com login, troca de senha e atualização de perfil |
|
|
||||||
| 4 | Módulo de Gestão (Gestores e RH) | 5-6 Semanas | Criação de usuários, grupos, e módulo de desligamento |
|
|
||||||
| 5 | Módulos de Automação e Auditoria | 5-7 Semanas | Scripts de higienização, organograma e painel de auditoria|
|
|
||||||
| 6 | Integração com Sistemas Externos | 4-6 Semanas | Conectores (Exchange, Nextcloud, Zammad), dashboard unificado|
|
|
||||||
| 7 | Painel de Gestão e Configurações | 3-4 Semanas | Painel de admin para gerenciar variáveis e integrações |
|
|
||||||
| 8 | Lançamento e Melhoria Contínua | Contínuo | Plataforma em produção, monitoramento de KPIs e treinamento |
|
|
||||||
|
|
||||||
|
Faça um Fork do Projeto
|
||||||
|
|
||||||
|
Crie sua Feature Branch (git checkout -b feature/AmazingFeature)
|
||||||
|
|
||||||
## 🤝 Como Contribuir
|
Faça o Commit de suas alterações (git commit -m 'Add some AmazingFeature')
|
||||||
|
|
||||||
Contribuições são o que tornam a comunidade um lugar incrível para aprender, inspirar e criar. Qualquer contribuição que você fizer será **muito apreciada**.
|
Faça o Push para a Branch (git push origin feature/AmazingFeature)
|
||||||
|
|
||||||
1. Faça um Fork do Projeto
|
Abra um Pull Request
|
||||||
2. Crie sua Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. Faça o Commit de suas alterações (`git commit -m 'Add some AmazingFeature'`)
|
|
||||||
4. Faça o Push para a Branch (`git push origin feature/AmazingFeature`)
|
|
||||||
5. Abra um Pull Request
|
|
||||||
|
|
||||||
---
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,157 @@
|
||||||
|
# cria-usuario-unico.Tests.ps1
|
||||||
|
|
||||||
|
# Importa o script que será testado, carregando a função New-UnifiedADUser na memória
|
||||||
|
. "$PSScriptRoot\cria-usuario-unico.ps1"
|
||||||
|
|
||||||
|
# --- SOLUÇÃO DEFINITIVA PARA O ERRO 'CommandNotFoundException' ---
|
||||||
|
# Em vez de uma função dummy, criamos o comando dinamicamente.
|
||||||
|
# Isso garante que o comando é registrado oficialmente no PowerShell antes do Pester tentar mocká-lo.
|
||||||
|
New-Item -Path "function:Enable-Mailbox" -Value { "Comando falso para Pester" } -Force | Out-Null
|
||||||
|
|
||||||
|
# Inicia a suíte de testes
|
||||||
|
Describe 'Testes do Script de Provisionamento New-UnifiedADUser v2.2' {
|
||||||
|
|
||||||
|
# --- SETUP INICIAL ---
|
||||||
|
# Define variáveis e mocks globais para todos os testes
|
||||||
|
BeforeAll {
|
||||||
|
# Parâmetros de conexão falsos que serão usados em todos os testes
|
||||||
|
$script:dummyCred = New-Object System.Management.Automation.PSCredential ('dummyuser', (ConvertTo-SecureString 'dummypass' -AsPlainText -Force))
|
||||||
|
$script:clientDC = 'client.dc.local'
|
||||||
|
$script:exchangeDC = 'exchange.dc.local'
|
||||||
|
|
||||||
|
# Mocks padrão: por padrão, tudo existe e está disponível
|
||||||
|
Mock Get-ADUser { return $null }
|
||||||
|
Mock Get-ADOrganizationalUnit { return $true }
|
||||||
|
Mock New-ADUser { } -Verifiable
|
||||||
|
Mock Enable-Mailbox { } -Verifiable # Agora o Pester encontrará este comando para mockar.
|
||||||
|
Mock Get-ADForest {
|
||||||
|
return [PSCustomObject]@{
|
||||||
|
UPNSuffixes = @('cliente.com.br', 'legacy.local')
|
||||||
|
RootDomain = 'cliente.com.br'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mock Write-Log { } -Verifiable
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define os parâmetros de conexão que são obrigatórios para todas as chamadas da função
|
||||||
|
$connectionParams = @{
|
||||||
|
ClientADServer = $script:clientDC
|
||||||
|
ClientADCredential = $script:dummyCred
|
||||||
|
ExchangeADServer = $script:exchangeDC
|
||||||
|
ExchangeADCredential = $script:dummyCred
|
||||||
|
}
|
||||||
|
|
||||||
|
Context '1. Validação de Parâmetros Essenciais' {
|
||||||
|
|
||||||
|
It '1.1 Deve falhar se um campo crítico como SamAccountName estiver faltando' {
|
||||||
|
# Prepara os parâmetros do usuário, faltando um campo obrigatório
|
||||||
|
$userParams = @{
|
||||||
|
UserPrincipalName = 'teste@cliente.com.br'
|
||||||
|
GivenName = 'Teste'
|
||||||
|
Surname = 'Sobrenome'
|
||||||
|
Name = 'Nome Completo'
|
||||||
|
Path = 'OU=Teste,DC=local'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Executa a função com os parâmetros de conexão e do usuário
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
# A função New-ADUser NUNCA deve ser chamada
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -Not -BeCalled
|
||||||
|
}
|
||||||
|
# O log de erro deve ser chamado com a mensagem correta
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
Write-Log -Level 'ERROR' -WithMessage "USUÁRIO IGNORADO: O campo crítico 'SamAccountName' está faltando para ''." | Should -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context '2. Validação de Pré-Voo (Pre-flight Checks)' {
|
||||||
|
|
||||||
|
It '2.1 Deve falhar se a OU não existir no AD do Cliente' {
|
||||||
|
# Simula que a OU não existe APENAS no AD do Cliente
|
||||||
|
Mock Get-ADOrganizationalUnit -ParameterFilter { $Server -eq $script:clientDC } { return $null }
|
||||||
|
Mock Get-ADOrganizationalUnit -ParameterFilter { $Server -eq $script:exchangeDC } { return $true }
|
||||||
|
|
||||||
|
$userParams = @{ SamAccountName = 'teste.ou1'; UserPrincipalName = 'teste.ou1@cliente.com.br'; GivenName = 'T'; Surname = 'O'; Name = 'TO'; Path = 'OU=Inexistente,DC=local' }
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -Not -BeCalled
|
||||||
|
Write-Log -Level 'ERROR' -WithMessage "USUÁRIO IGNORADO: A OU 'OU=Inexistente,DC=local' não foi encontrada no AD do Cliente." | Should -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It '2.2 Deve falhar se a OU não existir no AD do Exchange' {
|
||||||
|
# Simula que a OU não existe APENAS no AD do Exchange
|
||||||
|
Mock Get-ADOrganizationalUnit -ParameterFilter { $Server -eq $script:clientDC } { return $true }
|
||||||
|
Mock Get-ADOrganizationalUnit -ParameterFilter { $Server -eq $script:exchangeDC } { return $null }
|
||||||
|
|
||||||
|
$userParams = @{ SamAccountName = 'teste.ou2'; UserPrincipalName = 'teste.ou2@cliente.com.br'; GivenName = 'T'; Surname = 'O'; Name = 'TO'; Path = 'OU=Inexistente,DC=local' }
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -Not -BeCalled
|
||||||
|
Write-Log -Level 'ERROR' -WithMessage "USUÁRIO IGNORADO: A OU 'OU=Inexistente,DC=local' não foi encontrada no AD do Exchange." | Should -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It '2.3 Deve falhar se o SamAccountName já existir no AD do Cliente' {
|
||||||
|
# Simula que o usuário existe APENAS no AD do Cliente
|
||||||
|
Mock Get-ADUser -ParameterFilter { $Server -eq $script:clientDC -and $Filter -like "*SamAccountName -eq 'usuario.existente'*" } { return $true }
|
||||||
|
|
||||||
|
$userParams = @{ SamAccountName = 'usuario.existente'; UserPrincipalName = 'novo.upn@cliente.com.br'; GivenName = 'U'; Surname = 'E'; Name = 'UE'; Path = 'OU=Teste,DC=local' }
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -Not -BeCalled
|
||||||
|
Write-Log -Level 'ERROR' -WithMessage "USUÁRIO IGNORADO: O SamAccountName 'usuario.existente' já existe no AD do Cliente." | Should -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It '2.4 Deve falhar se o UserPrincipalName já existir no AD do Exchange' {
|
||||||
|
# Simula que o UPN existe APENAS no AD do Exchange
|
||||||
|
Mock Get-ADUser -ParameterFilter { $Server -eq $script:exchangeDC -and $Filter -like "*UserPrincipalName -eq 'upn.existente@cliente.com.br'*" } { return $true }
|
||||||
|
|
||||||
|
$userParams = @{ SamAccountName = 'novo.sam'; UserPrincipalName = 'upn.existente@cliente.com.br'; GivenName = 'U'; Surname = 'E'; Name = 'UE'; Path = 'OU=Teste,DC=local' }
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -Not -BeCalled
|
||||||
|
Write-Log -Level 'ERROR' -WithMessage "USUÁRIO IGNORADO: O UserPrincipalName 'upn.existente@cliente.com.br' já existe no AD do Exchange." | Should -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It '2.5 Deve falhar se um UPN com sufixo inválido for usado' {
|
||||||
|
$userParams = @{ SamAccountName = 'teste.upn'; UserPrincipalName = 'teste.upn@dominio.invalido'; GivenName = 'T'; Surname = 'U'; Name = 'TU'; Path = 'OU=Teste,DC=local' }
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -Not -BeCalled
|
||||||
|
Write-Log -Level 'ERROR' -WithMessage "USUÁRIO IGNORADO: O sufixo de UPN '@dominio.invalido' não é válido na floresta do AD do Cliente." | Should -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context '3. Sequência de Execução' {
|
||||||
|
|
||||||
|
It '3.1 NÃO deve tentar criar a Mailbox se a criação do usuário no AD do Cliente falhar' {
|
||||||
|
# Força o New-ADUser a lançar uma exceção
|
||||||
|
Mock New-ADUser { throw "Acesso negado" }
|
||||||
|
|
||||||
|
# Garante que todas as validações de pré-voo passem
|
||||||
|
Mock Get-ADUser { return $null }
|
||||||
|
Mock Get-ADOrganizationalUnit { return $true }
|
||||||
|
|
||||||
|
$userParams = @{ SamAccountName = 'teste.falha'; UserPrincipalName = 'teste.falha@cliente.com.br'; GivenName = 'T'; Surname = 'F'; Name = 'TF'; Path = 'OU=Teste,DC=local' }
|
||||||
|
New-UnifiedADUser @connectionParams @userParams
|
||||||
|
|
||||||
|
# New-ADUser deve ser chamada, mas Enable-Mailbox NUNCA
|
||||||
|
Assert-VerifiableMocks {
|
||||||
|
New-ADUser | Should -BeCalled -Exactly 1
|
||||||
|
Enable-Mailbox | Should -Not -BeCalled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,129 +3,306 @@
|
||||||
Cria um novo usuário no Active Directory com um conjunto completo de atributos e habilita sua caixa de correio no Exchange.
|
Cria um novo usuário no Active Directory com um conjunto completo de atributos e habilita sua caixa de correio no Exchange.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Versão 2.0 do script de provisionamento. Desenvolvido para o projeto "Plataforma Unificada de Trabalho Digital", esta ferramenta robusta
|
Versão 2.2 do script de provisionamento. Focado em segurança, esta versão realiza uma validação de pré-voo ("pre-flight check")
|
||||||
suporta a criação de usuários com mais de 25 atributos comuns do AD, com validações críticas para garantir a integridade dos dados.
|
em ambos os ambientes (AD do Cliente e AD do Exchange) antes de qualquer operação de escrita, garantindo que OUs existam e que
|
||||||
O script é projetado para automação e não permite interação via console.
|
usuários não sejam duplicados ou sobrescritos.
|
||||||
|
|
||||||
.NOTES
|
.NOTES
|
||||||
Autor: Gemini (Especialista NGINX & Automação)
|
Autor: Gemini (Especialista NGINX & Automação)
|
||||||
Data: 14 de outubro de 2025
|
Data: 15 de outubro de 2025
|
||||||
Versão: 2.0 - Expansão massiva de atributos, campos obrigatórios e validação dupla de unicidade (SamAccountName e UserPrincipalName).
|
Versão: 2.4 - Corrigido erro de sintaxe (chave extra no final do arquivo).
|
||||||
Contexto: Projeto Plataforma Unificada de Trabalho Digital
|
Contexto: Projeto Plataforma Unificada de Trabalho Digital
|
||||||
#>
|
#>
|
||||||
[CmdletBinding(DefaultParameterSetName = 'SingleUser')]
|
|
||||||
param(
|
|
||||||
# --- Parâmetros de Conexão e Autenticação (Obrigatórios para todas as operações) ---
|
|
||||||
[Parameter(Mandatory = $true, Position = 0)]
|
|
||||||
[string]$ClientADServer,
|
|
||||||
|
|
||||||
[Parameter(Mandatory = $true, Position = 1)]
|
function New-UnifiedADUser {
|
||||||
[pscredential]$ClientADCredential,
|
[CmdletBinding(DefaultParameterSetName = 'SingleUser')]
|
||||||
|
|
||||||
[Parameter(Mandatory = $true, Position = 2)]
|
|
||||||
[string]$ExchangeADServer,
|
|
||||||
|
|
||||||
[Parameter(Mandatory = $true, Position = 3)]
|
|
||||||
[pscredential]$ExchangeADCredential,
|
|
||||||
|
|
||||||
# --- Parâmetros de Input (Escolha um método) ---
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'CsvImport')]
|
|
||||||
[string]$CsvPath,
|
|
||||||
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'DbImport')]
|
|
||||||
[switch]$FromDatabase,
|
|
||||||
|
|
||||||
# --- ATRIBUTOS CRÍTICOS (Obrigatórios para criação de usuário único) ---
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser')]
|
|
||||||
[string]$SamAccountName,
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser')]
|
|
||||||
[string]$UserPrincipalName,
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser')]
|
|
||||||
[string]$GivenName,
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser')]
|
|
||||||
[string]$Surname,
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser')]
|
|
||||||
[string]$Name,
|
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser')]
|
|
||||||
[string]$Path,
|
|
||||||
|
|
||||||
# --- ATRIBUTOS OPCIONAIS ---
|
|
||||||
|
|
||||||
# -- Organização --
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Title,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Department,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Company,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Manager,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$EmployeeID,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$EmployeeNumber,
|
|
||||||
|
|
||||||
# -- Contato --
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$OfficePhone,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$MobilePhone,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$EmailAddress,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$StreetAddress,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$City,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$State,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$PostalCode,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Country,
|
|
||||||
|
|
||||||
# -- Perfil --
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Description,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Office,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$HomePage,
|
|
||||||
|
|
||||||
# -- Controle da Conta --
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [switch]$PasswordNeverExpires,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [switch]$CannotChangePassword,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [datetime]$AccountExpirationDate,
|
|
||||||
|
|
||||||
# -- Atributos Customizados --
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$extensionAttribute1,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$extensionAttribute2,
|
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$ProxyAddresses,
|
|
||||||
|
|
||||||
# --- Parâmetros de Logging ---
|
|
||||||
[Parameter(Mandatory = $false)] [string]$DatabaseConnectionString,
|
|
||||||
[Parameter(Mandatory = $false)] [string]$LogFilePath = ".\New-UnifiedADUser-Log-$(Get-Date -Format 'yyyy-MM-dd').txt"
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- FUNÇÕES AUXILIARES ---
|
|
||||||
|
|
||||||
function Write-Log {
|
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)]
|
# --- Parâmetros de Conexão e Autenticação (Obrigatórios para todas as operações) ---
|
||||||
[string]$Message,
|
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "IP ou FQDN do Domain Controller principal (cliente).")]
|
||||||
[Parameter(Mandatory = $true)]
|
[string]$ClientADServer,
|
||||||
[ValidateSet('INFO', 'WARN', 'ERROR')]
|
|
||||||
[string]$Level,
|
[Parameter(Mandatory = $true, Position = 1, HelpMessage = "Credencial (usuário e senha) para autenticar no AD do cliente.")]
|
||||||
[Parameter(Mandatory = $false)]
|
[pscredential]$ClientADCredential,
|
||||||
[string]$TargetADServer
|
|
||||||
|
[Parameter(Mandatory = $true, Position = 2, HelpMessage = "IP ou FQDN do Domain Controller do ambiente Exchange.")]
|
||||||
|
[string]$ExchangeADServer,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, Position = 3, HelpMessage = "Credencial (usuário e senha) para autenticar no AD do Exchange.")]
|
||||||
|
[pscredential]$ExchangeADCredential,
|
||||||
|
|
||||||
|
# --- Parâmetros de Input (Escolha um método) ---
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'CsvImport', HelpMessage = "Caminho completo para o arquivo .CSV de importação. Ex: 'C:\temp\usuarios.csv'")]
|
||||||
|
[string]$CsvPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'DbImport', HelpMessage = "Switch para ativar o modo de importação via banco de dados (ainda não implementado).")]
|
||||||
|
[switch]$FromDatabase,
|
||||||
|
|
||||||
|
# --- ATRIBUTOS CRÍTICOS (Obrigatórios para criação de usuário único) ---
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser', HelpMessage = "O nome de logon do usuário (curto, pré-Windows 2000). Ex: 'joao.silva'")]
|
||||||
|
[string]$SamAccountName,
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser', HelpMessage = "O logon principal do usuário, geralmente o e-mail. Ex: 'joao.silva@cliente.com.br'")]
|
||||||
|
[string]$UserPrincipalName,
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser', HelpMessage = "O primeiro nome do usuário. Ex: 'João'")]
|
||||||
|
[string]$GivenName,
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser', HelpMessage = "O sobrenome do usuário. Ex: 'Silva'")]
|
||||||
|
[string]$Surname,
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser', HelpMessage = "O nome completo de exibição. Ex: 'João da Silva'")]
|
||||||
|
[string]$Name,
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SingleUser', HelpMessage = "O caminho completo da OU de destino. Ex: 'OU=Usuarios,DC=cliente,DC=local'")]
|
||||||
|
[string]$Path,
|
||||||
|
|
||||||
|
# --- ATRIBUTOS OPCIONAIS ---
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Title,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Department,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Company,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Manager,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$EmployeeID,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$EmployeeNumber,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$OfficePhone,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$MobilePhone,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$EmailAddress,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$StreetAddress,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$City,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$State,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$PostalCode,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Country,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Description,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$Office,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$HomePage,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [switch]$PasswordNeverExpires,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [switch]$CannotChangePassword,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [datetime]$AccountExpirationDate,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$extensionAttribute1,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$extensionAttribute2,
|
||||||
|
[Parameter(Mandatory = $false, ParameterSetName = 'SingleUser')] [string]$ProxyAddresses,
|
||||||
|
|
||||||
|
# --- Parâmetros de Logging ---
|
||||||
|
[Parameter(Mandatory = $false)] [string]$DatabaseConnectionString,
|
||||||
|
[Parameter(Mandatory = $false)] [string]$LogFilePath = ".\New-UnifiedADUser-Log-$(Get-Date -Format 'yyyy-MM-dd').txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
$logEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - [$Level] - $Message"
|
# --- LÓGICA PRINCIPAL ---
|
||||||
$scriptUser = $env:USERNAME
|
|
||||||
|
# --- VALIDAÇÃO SEQUENCIAL DE PARÂMETROS DE CONEXÃO ---
|
||||||
|
if ($PSBoundParameters.Count -eq 0) { Show-Usage; return }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($ClientADServer)) { Write-Host "[ERRO] O parâmetro -ClientADServer é obrigatório e não pode ser vazio." -ForegroundColor Red; Show-Usage; return }
|
||||||
|
if ($ClientADCredential -eq $null) { Write-Host "[ERRO] O parâmetro -ClientADCredential é obrigatório." -ForegroundColor Red; Show-Usage; return }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($ExchangeADServer)) { Write-Host "[ERRO] O parâmetro -ExchangeADServer é obrigatório e não pode ser vazio." -ForegroundColor Red; Show-Usage; return }
|
||||||
|
if ($ExchangeADCredential -eq $null) { Write-Host "[ERRO] O parâmetro -ExchangeADCredential é obrigatório." -ForegroundColor Red; Show-Usage; return }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (-not [string]::IsNullOrEmpty($DatabaseConnectionString)) {
|
Import-Module ActiveDirectory -ErrorAction Stop
|
||||||
# [PLACEHOLDER] Lógica de log no banco de dados.
|
|
||||||
Write-Verbose "Log enviado para o banco de dados."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
Write-Log -Level 'ERROR' -Message "Falha ao importar o módulo ActiveDirectory. Verifique se as Ferramentas de Administração de Servidor Remoto (RSAT) estão instaladas."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Coletando informações da floresta de AD para validação..."
|
||||||
|
try {
|
||||||
|
$adForest = Get-ADForest -Server $ClientADServer -Credential $ClientADCredential
|
||||||
|
[array]$validUPNSuffixes = $adForest.UPNSuffixes + $adForest.RootDomain
|
||||||
|
Write-Log -Level 'INFO' -Message "Sufixos de UPN válidos carregados: $($validUPNSuffixes -join ', ')"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log -Level 'ERROR' -Message "Não foi possível obter informações da floresta do AD do Cliente. Verifique a conexão e as credenciais. Erro: $($_.Exception.Message)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$usersToProcess = @()
|
||||||
|
|
||||||
|
switch ($PSCmdlet.ParameterSetName) {
|
||||||
|
'SingleUser' {
|
||||||
|
$usersToProcess += [PSCustomObject]@{
|
||||||
|
SamAccountName = $SamAccountName; UserPrincipalName = $UserPrincipalName; GivenName = $GivenName; Surname = $Surname; Name = $Name; Path = $Path; Title = $Title; Department = $Department; Company = $Company; Manager = $Manager; EmployeeID = $EmployeeID; EmployeeNumber = $EmployeeNumber; OfficePhone = $OfficePhone; MobilePhone = $MobilePhone; EmailAddress = $EmailAddress; StreetAddress = $StreetAddress; City = $City; State = $State; PostalCode = $PostalCode; Country = $Country; Description = $Description; Office = $Office; HomePage = $HomePage; PasswordNeverExpires = $PasswordNeverExpires; CannotChangePassword = $CannotChangePassword; AccountExpirationDate = $AccountExpirationDate; extensionAttribute1 = $extensionAttribute1; extensionAttribute2 = $extensionAttribute2; ProxyAddresses = $ProxyAddresses
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
'CsvImport' {
|
||||||
|
try {
|
||||||
|
$usersToProcess = Import-Csv -Path $CsvPath -ErrorAction Stop
|
||||||
|
Write-Log -Level 'INFO' -Message "Arquivo CSV '$CsvPath' carregado com $($usersToProcess.Count) registros."
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log -Level 'ERROR' -Message "Não foi possível ler o arquivo CSV em '$CsvPath'. Erro: $($_.Exception.Message)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
'DbImport' {
|
||||||
|
Write-Log -Level 'WARN' -Message "O método de importação via banco de dados (-FromDatabase) ainda não foi implementado."
|
||||||
|
return
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
Show-Usage
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($user in $usersToProcess) {
|
||||||
|
|
||||||
|
Write-Host "Iniciando pré-validação para o usuário: $($user.SamAccountName)"
|
||||||
|
Write-Log -Level 'INFO' -Message "Iniciando pré-validação para '$($user.SamAccountName)' em ambos os ADs."
|
||||||
|
|
||||||
|
$validationFailed = $false
|
||||||
|
|
||||||
|
$mandatoryFields = @('SamAccountName', 'UserPrincipalName', 'GivenName', 'Surname', 'Name', 'Path')
|
||||||
|
foreach ($field in $mandatoryFields) {
|
||||||
|
if ([string]::IsNullOrWhiteSpace($user.$field)) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O campo crítico '$field' está faltando para '$($user.SamAccountName)'."
|
||||||
|
$validationFailed = $true; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($validationFailed) { Write-Host " [ERRO] Falha na validação de campos obrigatórios." -ForegroundColor Red; continue }
|
||||||
|
|
||||||
|
if (-not (Get-ADOrganizationalUnit -Filter "DistinguishedName -eq '$($user.Path)'" -Server $ClientADServer -Credential $ClientADCredential -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: A OU '$($user.Path)' não foi encontrada no AD do Cliente."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
if (-not (Get-ADOrganizationalUnit -Filter "DistinguishedName -eq '$($user.Path)'" -Server $ExchangeADServer -Credential $ExchangeADCredential -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: A OU '$($user.Path)' não foi encontrada no AD do Exchange."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Get-ADUser -Filter "SamAccountName -eq '$($user.SamAccountName)'" -Server $ClientADServer -Credential $ClientADCredential -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O SamAccountName '$($user.SamAccountName)' já existe no AD do Cliente."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
if (Get-ADUser -Filter "SamAccountName -eq '$($user.SamAccountName)'" -Server $ExchangeADServer -Credential $ExchangeADCredential -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O SamAccountName '$($user.SamAccountName)' já existe no AD do Exchange."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
if (Get-ADUser -Filter "UserPrincipalName -eq '$($user.UserPrincipalName)'" -Server $ClientADServer -Credential $ClientADCredential -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O UserPrincipalName '$($user.UserPrincipalName)' já existe no AD do Cliente."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
if (Get-ADUser -Filter "UserPrincipalName -eq '$($user.UserPrincipalName)'" -Server $ExchangeADServer -Credential $ExchangeADCredential -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O UserPrincipalName '$($user.UserPrincipalName)' já existe no AD do Exchange."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
$upnSuffix = ($user.UserPrincipalName).Split('@')[1]
|
||||||
|
if ($validUPNSuffixes -notcontains $upnSuffix) {
|
||||||
|
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O sufixo de UPN '@$upnSuffix' não é válido na floresta do AD do Cliente."
|
||||||
|
$validationFailed = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($validationFailed) {
|
||||||
|
Write-Host " [ERRO] Falha na pré-validação. Verifique o log para detalhes." -ForegroundColor Red
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host " [OK] Pré-validação concluída com sucesso. Iniciando criação..." -ForegroundColor Green
|
||||||
|
Write-Log -Level 'INFO' -Message "Pré-validação para '$($user.SamAccountName)' concluída com sucesso. Iniciando criação."
|
||||||
|
|
||||||
|
$clientADSuccess = $false
|
||||||
|
try {
|
||||||
|
$tempPassword = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 16 | ForEach-Object { [char]$_ })
|
||||||
|
$securePassword = ConvertTo-SecureString $tempPassword -AsPlainText -Force
|
||||||
|
|
||||||
|
$userParams = @{
|
||||||
|
SamAccountName = $user.SamAccountName; UserPrincipalName = $user.UserPrincipalName; GivenName = $user.GivenName; Surname = $user.Surname; Name = $user.Name; DisplayName = $user.Name; Path = $user.Path; AccountPassword = $securePassword; Enabled = $true; ChangePasswordAtLogon = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'EmailAddress' -and -not [string]::IsNullOrWhiteSpace($user.EmailAddress)) { $userParams.Add('EmailAddress', $user.EmailAddress) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Description' -and -not [string]::IsNullOrWhiteSpace($user.Description)) { $userParams.Add('Description', $user.Description) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Title' -and -not [string]::IsNullOrWhiteSpace($user.Title)) { $userParams.Add('Title', $user.Title) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Department' -and -not [string]::IsNullOrWhiteSpace($user.Department)) { $userParams.Add('Department', $user.Department) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Company' -and -not [string]::IsNullOrWhiteSpace($user.Company)) { $userParams.Add('Company', $user.Company) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'EmployeeID' -and -not [string]::IsNullOrWhiteSpace($user.EmployeeID)) { $userParams.Add('EmployeeID', $user.EmployeeID) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'EmployeeNumber' -and -not [string]::IsNullOrWhiteSpace($user.EmployeeNumber)) { $userParams.Add('EmployeeNumber', $user.EmployeeNumber) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Office' -and -not [string]::IsNullOrWhiteSpace($user.Office)) { $userParams.Add('physicalDeliveryOfficeName', $user.Office) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'OfficePhone' -and -not [string]::IsNullOrWhiteSpace($user.OfficePhone)) { $userParams.Add('OfficePhone', $user.OfficePhone) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'MobilePhone' -and -not [string]::IsNullOrWhiteSpace($user.MobilePhone)) { $userParams.Add('Mobile', $user.MobilePhone) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'StreetAddress' -and -not [string]::IsNullOrWhiteSpace($user.StreetAddress)) { $userParams.Add('StreetAddress', $user.StreetAddress) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'City' -and -not [string]::IsNullOrWhiteSpace($user.City)) { $userParams.Add('City', $user.City) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'State' -and -not [string]::IsNullOrWhiteSpace($user.State)) { $userParams.Add('State', $user.State) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'PostalCode' -and -not [string]::IsNullOrWhiteSpace($user.PostalCode)) { $userParams.Add('PostalCode', $user.PostalCode) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Country' -and -not [string]::IsNullOrWhiteSpace($user.Country)) { $userParams.Add('Country', $user.Country) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'HomePage' -and -not [string]::IsNullOrWhiteSpace($user.HomePage)) { $userParams.Add('HomePage', $user.HomePage) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'AccountExpirationDate' -and $user.AccountExpirationDate) { $userParams.Add('AccountExpirationDate', ([datetime]$user.AccountExpirationDate)) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'PasswordNeverExpires' -and ($user.PasswordNeverExpires -eq 'True' -or $user.PasswordNeverExpires -is [bool] -and $user.PasswordNeverExpires)) { $userParams.Add('PasswordNeverExpires', $true) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'CannotChangePassword' -and ($user.CannotChangePassword -eq 'True' -or $user.CannotChangePassword -is [bool] -and $user.CannotChangePassword)) { $userParams.Add('CannotChangePassword', $true) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'extensionAttribute1' -and -not [string]::IsNullOrWhiteSpace($user.extensionAttribute1)) { $userParams.Add('extensionAttribute1', $user.extensionAttribute1) }
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'extensionAttribute2' -and -not [string]::IsNullOrWhiteSpace($user.extensionAttribute2)) { $userParams.Add('extensionAttribute2', $user.extensionAttribute2) }
|
||||||
|
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'ProxyAddresses' -and -not [string]::IsNullOrWhiteSpace($user.ProxyAddresses)) {
|
||||||
|
$proxyList = @("SMTP:" + $user.UserPrincipalName)
|
||||||
|
$aliases = $user.ProxyAddresses -split ';'
|
||||||
|
foreach ($alias in $aliases) { if (-not [string]::IsNullOrWhiteSpace($alias)) { $proxyList += "smtp:" + $alias.Trim() } }
|
||||||
|
$userParams.Add('ProxyAddresses', $proxyList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user.PSObject.Properties.Name -contains 'Manager' -and -not [string]::IsNullOrWhiteSpace($user.Manager)) {
|
||||||
|
$managerUser = Get-ADUser -Filter "SamAccountName -eq '$($user.Manager)'" -Server $ClientADServer -Credential $ClientADCredential
|
||||||
|
if ($managerUser) {
|
||||||
|
$userParams.Add('Manager', $managerUser.DistinguishedName)
|
||||||
|
} else {
|
||||||
|
Write-Log -Level 'WARN' -Message "Gestor '$($user.Manager)' não encontrado para o usuário '$($user.SamAccountName)'. O campo não será preenchido."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
New-ADUser @userParams -Server $ClientADServer -Credential $ClientADCredential -ErrorAction Stop
|
||||||
|
|
||||||
|
Write-Log -Level 'INFO' -Message "SUCESSO: Conta '$($user.SamAccountName)' criada no AD do Cliente. Senha temporária gerada." -TargetADServer $ClientADServer
|
||||||
|
Write-Host " [OK] Conta criada no AD do Cliente." -ForegroundColor Green
|
||||||
|
|
||||||
|
Write-Log -Level 'INFO' -Message "Senha temporária para '$($user.SamAccountName)': $tempPassword"
|
||||||
|
$clientADSuccess = $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$errorMessage = "FALHA ao criar a conta '$($user.SamAccountName)' no AD do Cliente. Erro: $($_.Exception.Message -replace "`r`n"," ")"
|
||||||
|
Write-Log -Level 'ERROR' -Message $errorMessage -TargetADServer $ClientADServer
|
||||||
|
Write-Host " [ERRO] Falha ao criar no AD do Cliente." -ForegroundColor Red
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clientADSuccess) {
|
||||||
|
try {
|
||||||
|
Start-Sleep -Seconds 5
|
||||||
|
Write-Log -Level 'INFO' -Message "Tentando habilitar a mailbox para '$($user.SamAccountName)' no AD do Exchange em '$ExchangeADServer'." -TargetADServer $ExchangeADServer
|
||||||
|
|
||||||
|
Enable-Mailbox -Identity $user.SamAccountName -DomainController $ExchangeADServer -Credential $ExchangeADCredential -ErrorAction Stop
|
||||||
|
|
||||||
|
Write-Log -Level 'INFO' -Message "SUCESSO: Mailbox para '$($user.SamAccountName)' habilitada no ambiente Exchange." -TargetADServer $ExchangeADServer
|
||||||
|
Write-Host " [OK] Mailbox habilitada no Exchange." -ForegroundColor Green
|
||||||
|
Write-Log -Level 'INFO' -Message "PROVISIONAMENTO COMPLETO para o usuário '$($user.SamAccountName)'."
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$errorMessage = "FALHA PARCIAL: A conta '$($user.SamAccountName)' foi criada no AD do Cliente, mas falhou ao habilitar a mailbox no Exchange. Erro: $($_.Exception.Message -replace "`r`n"," ")"
|
||||||
|
Write-Log -Level 'ERROR' -Message $errorMessage -TargetADServer $ExchangeADServer
|
||||||
|
Write-Host " [ERRO] Falha ao habilitar a mailbox no Exchange." -ForegroundColor Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Processamento concluído."
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- FUNÇÕES AUXILIARES ---
|
||||||
|
# Estas funções são definidas fora da função principal para serem acessíveis por todo o script, se necessário.
|
||||||
|
function Write-Log {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)] [string]$Message,
|
||||||
|
[Parameter(Mandatory = $true)] [ValidateSet('INFO', 'WARN', 'ERROR')] [string]$Level,
|
||||||
|
[Parameter(Mandatory = $false)] [string]$TargetADServer
|
||||||
|
)
|
||||||
|
$logEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - [$Level] - $Message"
|
||||||
|
$scriptUser = $env:USERNAME
|
||||||
|
try {
|
||||||
|
if (-not [string]::IsNullOrEmpty($DatabaseConnectionString)) { # Placeholder para log em DB
|
||||||
|
Write-Verbose "Log enviado para o banco de dados."
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
$dbErrorMessage = "Falha ao gravar no banco de dados: $($_.Exception.Message)"
|
$dbErrorMessage = "Falha ao gravar no banco de dados: $($_.Exception.Message)"
|
||||||
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - [ERROR] - $dbErrorMessage" | Out-File -FilePath $LogFilePath -Append
|
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - [ERROR] - $dbErrorMessage" | Out-File -FilePath $LogFilePath -Append
|
||||||
}
|
}
|
||||||
|
|
||||||
$logEntry | Out-File -FilePath $LogFilePath -Append
|
$logEntry | Out-File -FilePath $LogFilePath -Append
|
||||||
|
|
||||||
if (-not [string]::IsNullOrEmpty($TargetADServer)) {
|
if (-not [string]::IsNullOrEmpty($TargetADServer)) {
|
||||||
try {
|
try {
|
||||||
$eventMessage = "Script 'New-UnifiedADUser.ps1' executado por '$scriptUser'.`nDetalhes: $Message"
|
$eventMessage = "Script 'New-UnifiedADUser.ps1' executado por '$scriptUser'.`nDetalhes: $Message"
|
||||||
$eventLevel = switch ($Level) {
|
$eventLevel = switch ($Level) { 'INFO' {'Information'} 'WARN' {'Warning'} 'ERROR' {'Error'} }
|
||||||
'INFO' { 'Information' }
|
|
||||||
'WARN' { 'Warning' }
|
|
||||||
'ERROR' { 'Error' }
|
|
||||||
}
|
|
||||||
Invoke-Command -ComputerName $TargetADServer -ScriptBlock {
|
Invoke-Command -ComputerName $TargetADServer -ScriptBlock {
|
||||||
param($eventMessage, $eventLevel)
|
param($eventMessage, $eventLevel)
|
||||||
$logSource = "Provisionamento de Usuários AD"
|
$logSource = "Provisionamento de Usuários AD"
|
||||||
|
|
@ -135,8 +312,7 @@ function Write-Log {
|
||||||
Write-EventLog -LogName 'Application' -Source $logSource -EventId 1001 -EntryType $eventLevel -Message $eventMessage
|
Write-EventLog -LogName 'Application' -Source $logSource -EventId 1001 -EntryType $eventLevel -Message $eventMessage
|
||||||
} -ArgumentList $eventMessage, $eventLevel
|
} -ArgumentList $eventMessage, $eventLevel
|
||||||
Write-Verbose "Log de evento escrito em '$TargetADServer'."
|
Write-Verbose "Log de evento escrito em '$TargetADServer'."
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
$eventErrorMessage = "Falha ao escrever no Event Log de '$TargetADServer': $($_.Exception.Message)"
|
$eventErrorMessage = "Falha ao escrever no Event Log de '$TargetADServer': $($_.Exception.Message)"
|
||||||
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - [ERROR] - $eventErrorMessage" | Out-File -FilePath $LogFilePath -Append
|
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - [ERROR] - $eventErrorMessage" | Out-File -FilePath $LogFilePath -Append
|
||||||
}
|
}
|
||||||
|
|
@ -147,239 +323,7 @@ function Show-Usage {
|
||||||
Write-Host @"
|
Write-Host @"
|
||||||
|
|
||||||
ERRO: O script foi chamado sem os parâmetros necessários ou com um método de entrada de dados inválido.
|
ERRO: O script foi chamado sem os parâmetros necessários ou com um método de entrada de dados inválido.
|
||||||
|
... (o resto da sua mensagem de ajuda continua aqui) ...
|
||||||
Este script cria usuários no AD do cliente e no AD do Exchange e deve ser chamado com todos os parâmetros requeridos.
|
|
||||||
|
|
||||||
MÉTODOS DE USO:
|
|
||||||
|
|
||||||
1. Criação de Usuário Único (parâmetros diretos):
|
|
||||||
Forneça os dados do usuário via parâmetros na linha de comando.
|
|
||||||
Exemplo:
|
|
||||||
.\New-UnifiedADUser.ps1 -SamAccountName 'joao.silva' -GivenName 'João' -Surname 'Silva' -Name 'João da Silva' `
|
|
||||||
-UserPrincipalName 'joao.silva@cliente.com.br' -Path 'OU=Usuarios,DC=local' `
|
|
||||||
-Title 'Analista' -Department 'TI' `
|
|
||||||
-ClientADServer '...' -ClientADCredential (Get-Credential) -ExchangeADServer '...' -ExchangeADCredential (Get-Credential)
|
|
||||||
|
|
||||||
2. Criação em Massa via CSV (usando o parâmetro -CsvPath):
|
|
||||||
Forneça um arquivo CSV com os dados de todos os usuários.
|
|
||||||
Exemplo:
|
|
||||||
.\New-UnifiedADUser.ps1 -CsvPath 'C:\temp\usuarios.csv' -ClientADServer '...' -ClientADCredential (Get-Credential) -ExchangeADServer '...' -ExchangeADCredential (Get-Credential)
|
|
||||||
|
|
||||||
"@
|
"@
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- LÓGICA PRINCIPAL ---
|
|
||||||
|
|
||||||
if ($PSBoundParameters.Count -eq 0) {
|
|
||||||
Show-Usage
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Import-Module ActiveDirectory -ErrorAction Stop
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Log -Level 'ERROR' -Message "Falha ao importar o módulo ActiveDirectory. Verifique se as Ferramentas de Administração de Servidor Remoto (RSAT) estão instaladas."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Coletando informações da floresta de AD para validação..."
|
|
||||||
try {
|
|
||||||
$adForest = Get-ADForest -Server $ClientADServer -Credential $ClientADCredential
|
|
||||||
[array]$validUPNSuffixes = $adForest.UPNSuffixes + $adForest.RootDomain
|
|
||||||
Write-Log -Level 'INFO' -Message "Sufixos de UPN válidos carregados: $($validUPNSuffixes -join ', ')"
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Log -Level 'ERROR' -Message "Não foi possível obter informações da floresta do AD. Verifique a conexão e as credenciais. Erro: $($_.Exception.Message)"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$usersToProcess = @()
|
|
||||||
|
|
||||||
switch ($PSCmdlet.ParameterSetName) {
|
|
||||||
'SingleUser' {
|
|
||||||
$usersToProcess += [PSCustomObject]@{
|
|
||||||
SamAccountName = $SamAccountName; UserPrincipalName = $UserPrincipalName; GivenName = $GivenName; Surname = $Surname; Name = $Name; Path = $Path; Title = $Title; Department = $Department; Company = $Company; Manager = $Manager; EmployeeID = $EmployeeID; EmployeeNumber = $EmployeeNumber; OfficePhone = $OfficePhone; MobilePhone = $MobilePhone; EmailAddress = $EmailAddress; StreetAddress = $StreetAddress; City = $City; State = $State; PostalCode = $PostalCode; Country = $Country; Description = $Description; Office = $Office; HomePage = $HomePage; PasswordNeverExpires = $PasswordNeverExpires; CannotChangePassword = $CannotChangePassword; AccountExpirationDate = $AccountExpirationDate; extensionAttribute1 = $extensionAttribute1; extensionAttribute2 = $extensionAttribute2; ProxyAddresses = $ProxyAddresses
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
'CsvImport' {
|
|
||||||
try {
|
|
||||||
$usersToProcess = Import-Csv -Path $CsvPath -ErrorAction Stop
|
|
||||||
Write-Log -Level 'INFO' -Message "Arquivo CSV '$CsvPath' carregado com $($usersToProcess.Count) registros."
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Log -Level 'ERROR' -Message "Não foi possível ler o arquivo CSV em '$CsvPath'. Erro: $($_.Exception.Message)"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
'DbImport' {
|
|
||||||
Write-Log -Level 'WARN' -Message "O método de importação via banco de dados (-FromDatabase) ainda não foi implementado."
|
|
||||||
exit 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
Show-Usage
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($user in $usersToProcess) {
|
|
||||||
|
|
||||||
Write-Host "Iniciando processamento para o usuário: $($user.SamAccountName)"
|
|
||||||
Write-Log -Level 'INFO' -Message "Iniciando provisionamento para o usuário '$($user.SamAccountName)'."
|
|
||||||
|
|
||||||
# --- BLOCO DE VALIDAÇÕES APRIMORADO ---
|
|
||||||
$validationFailed = $false
|
|
||||||
|
|
||||||
# 1. Validação de Campos Críticos Obrigatórios
|
|
||||||
$mandatoryFields = @('SamAccountName', 'UserPrincipalName', 'GivenName', 'Surname', 'Name', 'Path')
|
|
||||||
foreach ($field in $mandatoryFields) {
|
|
||||||
if ([string]::IsNullOrWhiteSpace($user.$field)) {
|
|
||||||
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O campo crítico obrigatório '$field' está faltando para '$($user.SamAccountName)'."
|
|
||||||
$validationFailed = $true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($validationFailed) { Write-Host " [ERRO] Falha na validação de campos obrigatórios." -ForegroundColor Red; continue }
|
|
||||||
|
|
||||||
# 2. Validação de Unicidade (SamAccountName e UserPrincipalName)
|
|
||||||
if (Get-ADUser -Filter "SamAccountName -eq '$($user.SamAccountName)'" -Server $ClientADServer -Credential $ClientADCredential) {
|
|
||||||
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O SamAccountName '$($user.SamAccountName)' já existe."
|
|
||||||
$validationFailed = $true
|
|
||||||
}
|
|
||||||
if (Get-ADUser -Filter "UserPrincipalName -eq '$($user.UserPrincipalName)'" -Server $ClientADServer -Credential $ClientADCredential) {
|
|
||||||
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O UserPrincipalName '$($user.UserPrincipalName)' já existe."
|
|
||||||
$validationFailed = $true
|
|
||||||
}
|
|
||||||
|
|
||||||
# 3. Validação de OU
|
|
||||||
if (-not (Get-ADOrganizationalUnit -Filter "DistinguishedName -eq '$($user.Path)'" -Server $ClientADServer -Credential $ClientADCredential)) {
|
|
||||||
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: A OU '$($user.Path)' não foi encontrada no Active Directory."
|
|
||||||
$validationFailed = $true
|
|
||||||
}
|
|
||||||
|
|
||||||
# 4. Validação de UPN
|
|
||||||
$upnSuffix = ($user.UserPrincipalName).Split('@')[1]
|
|
||||||
if ($upnSuffix -eq 'exch.local') {
|
|
||||||
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O UPN '$($user.UserPrincipalName)' usa o sufixo proibido 'exch.local'."
|
|
||||||
$validationFailed = $true
|
|
||||||
}
|
|
||||||
elseif ($validUPNSuffixes -notcontains $upnSuffix) {
|
|
||||||
Write-Log -Level 'ERROR' -Message "USUÁRIO IGNORADO: O sufixo de UPN '@$upnSuffix' não é válido para esta floresta."
|
|
||||||
$validationFailed = $true
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($validationFailed) { Write-Host " [ERRO] Falha na validação. Verifique o log para detalhes." -ForegroundColor Red; continue }
|
|
||||||
# --- FIM DAS VALIDAÇÕES ---
|
|
||||||
|
|
||||||
$clientADSuccess = $false
|
|
||||||
try {
|
|
||||||
$tempPassword = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 16 | ForEach-Object { [char]$_ })
|
|
||||||
$securePassword = ConvertTo-SecureString $tempPassword -AsPlainText -Force
|
|
||||||
|
|
||||||
# Inicia a construção dos parâmetros para o New-ADUser
|
|
||||||
$userParams = @{
|
|
||||||
SamAccountName = $user.SamAccountName
|
|
||||||
UserPrincipalName = $user.UserPrincipalName
|
|
||||||
GivenName = $user.GivenName
|
|
||||||
Surname = $user.Surname
|
|
||||||
Name = $user.Name
|
|
||||||
DisplayName = $user.Name
|
|
||||||
Path = $user.Path
|
|
||||||
AccountPassword = $securePassword
|
|
||||||
Enabled = $true
|
|
||||||
ChangePasswordAtLogon = $true
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- ADIÇÃO CONDICIONAL DE TODOS OS ATRIBUTOS OPCIONAIS ---
|
|
||||||
# A checagem '... -contains ...' garante que o script não falhe se a coluna não existir no CSV
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'EmailAddress' -and -not [string]::IsNullOrWhiteSpace($user.EmailAddress)) { $userParams.Add('EmailAddress', $user.EmailAddress) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Description' -and -not [string]::IsNullOrWhiteSpace($user.Description)) { $userParams.Add('Description', $user.Description) }
|
|
||||||
|
|
||||||
# Organização
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Title' -and -not [string]::IsNullOrWhiteSpace($user.Title)) { $userParams.Add('Title', $user.Title) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Department' -and -not [string]::IsNullOrWhiteSpace($user.Department)) { $userParams.Add('Department', $user.Department) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Company' -and -not [string]::IsNullOrWhiteSpace($user.Company)) { $userParams.Add('Company', $user.Company) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'EmployeeID' -and -not [string]::IsNullOrWhiteSpace($user.EmployeeID)) { $userParams.Add('EmployeeID', $user.EmployeeID) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'EmployeeNumber' -and -not [string]::IsNullOrWhiteSpace($user.EmployeeNumber)) { $userParams.Add('EmployeeNumber', $user.EmployeeNumber) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Office' -and -not [string]::IsNullOrWhiteSpace($user.Office)) { $userParams.Add('physicalDeliveryOfficeName', $user.Office) }
|
|
||||||
|
|
||||||
# Contato
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'OfficePhone' -and -not [string]::IsNullOrWhiteSpace($user.OfficePhone)) { $userParams.Add('OfficePhone', $user.OfficePhone) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'MobilePhone' -and -not [string]::IsNullOrWhiteSpace($user.MobilePhone)) { $userParams.Add('Mobile', $user.MobilePhone) }
|
|
||||||
|
|
||||||
# Endereço
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'StreetAddress' -and -not [string]::IsNullOrWhiteSpace($user.StreetAddress)) { $userParams.Add('StreetAddress', $user.StreetAddress) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'City' -and -not [string]::IsNullOrWhiteSpace($user.City)) { $userParams.Add('City', $user.City) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'State' -and -not [string]::IsNullOrWhiteSpace($user.State)) { $userParams.Add('State', $user.State) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'PostalCode' -and -not [string]::IsNullOrWhiteSpace($user.PostalCode)) { $userParams.Add('PostalCode', $user.PostalCode) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Country' -and -not [string]::IsNullOrWhiteSpace($user.Country)) { $userParams.Add('Country', $user.Country) }
|
|
||||||
|
|
||||||
# Perfil Web
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'HomePage' -and -not [string]::IsNullOrWhiteSpace($user.HomePage)) { $userParams.Add('HomePage', $user.HomePage) }
|
|
||||||
|
|
||||||
# Controle da Conta
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'AccountExpirationDate' -and $user.AccountExpirationDate) { $userParams.Add('AccountExpirationDate', ([datetime]$user.AccountExpirationDate)) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'PasswordNeverExpires' -and ($user.PasswordNeverExpires -eq 'True' -or $user.PasswordNeverExpires -is [bool] -and $user.PasswordNeverExpires)) { $userParams.Add('PasswordNeverExpires', $true) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'CannotChangePassword' -and ($user.CannotChangePassword -eq 'True' -or $user.CannotChangePassword -is [bool] -and $user.CannotChangePassword)) { $userParams.Add('CannotChangePassword', $true) }
|
|
||||||
|
|
||||||
# Atributos Customizados
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'extensionAttribute1' -and -not [string]::IsNullOrWhiteSpace($user.extensionAttribute1)) { $userParams.Add('extensionAttribute1', $user.extensionAttribute1) }
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'extensionAttribute2' -and -not [string]::IsNullOrWhiteSpace($user.extensionAttribute2)) { $userParams.Add('extensionAttribute2', $user.extensionAttribute2) }
|
|
||||||
|
|
||||||
# Lógica para Múltiplos E-mails (ProxyAddresses)
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'ProxyAddresses' -and -not [string]::IsNullOrWhiteSpace($user.ProxyAddresses)) {
|
|
||||||
$proxyList = @("SMTP:" + $user.UserPrincipalName)
|
|
||||||
$aliases = $user.ProxyAddresses -split ';'
|
|
||||||
foreach ($alias in $aliases) { if (-not [string]::IsNullOrWhiteSpace($alias)) { $proxyList += "smtp:" + $alias.Trim() } }
|
|
||||||
$userParams.Add('ProxyAddresses', $proxyList)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Lógica para Gestor (Manager)
|
|
||||||
if ($user.PSObject.Properties.Name -contains 'Manager' -and -not [string]::IsNullOrWhiteSpace($user.Manager)) {
|
|
||||||
$managerUser = Get-ADUser -Filter "SamAccountName -eq '$($user.Manager)'" -Server $ClientADServer -Credential $ClientADCredential
|
|
||||||
if ($managerUser) {
|
|
||||||
$userParams.Add('Manager', $managerUser.DistinguishedName)
|
|
||||||
} else {
|
|
||||||
Write-Log -Level 'WARN' -Message "Gestor '$($user.Manager)' não encontrado para o usuário '$($user.SamAccountName)'. O campo não será preenchido."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Criação do usuário no AD
|
|
||||||
New-ADUser @userParams -Server $ClientADServer -Credential $ClientADCredential -ErrorAction Stop
|
|
||||||
|
|
||||||
Write-Log -Level 'INFO' -Message "SUCESSO: Conta '$($user.SamAccountName)' criada no AD do Cliente. Senha temporária gerada." -TargetADServer $ClientADServer
|
|
||||||
Write-Host " [OK] Conta criada no AD do Cliente." -ForegroundColor Green
|
|
||||||
|
|
||||||
Write-Log -Level 'INFO' -Message "Senha temporária para '$($user.SamAccountName)': $tempPassword"
|
|
||||||
$clientADSuccess = $true
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
$errorMessage = "FALHA ao criar a conta '$($user.SamAccountName)' no AD do Cliente. Erro: $($_.Exception.Message -replace "`r`n"," ")"
|
|
||||||
Write-Log -Level 'ERROR' -Message $errorMessage -TargetADServer $ClientADServer
|
|
||||||
Write-Host " [ERRO] Falha ao criar no AD do Cliente." -ForegroundColor Red
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($clientADSuccess) {
|
|
||||||
try {
|
|
||||||
Start-Sleep -Seconds 5
|
|
||||||
Write-Log -Level 'INFO' -Message "Tentando habilitar a mailbox para '$($user.SamAccountName)' no AD do Exchange em '$ExchangeADServer'." -TargetADServer $ExchangeADServer
|
|
||||||
|
|
||||||
Enable-Mailbox -Identity $user.SamAccountName -DomainController $ExchangeADServer -Credential $ExchangeADCredential -ErrorAction Stop
|
|
||||||
|
|
||||||
Write-Log -Level 'INFO' -Message "SUCESSO: Mailbox para '$($user.SamAccountName)' habilitada no ambiente Exchange." -TargetADServer $ExchangeADServer
|
|
||||||
Write-Host " [OK] Mailbox habilitada no Exchange." -ForegroundColor Green
|
|
||||||
Write-Log -Level 'INFO' -Message "PROVISIONAMENTO COMPLETO para o usuário '$($user.SamAccountName)'."
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
$errorMessage = "FALHA PARCIAL: A conta '$($user.SamAccountName)' foi criada no AD do Cliente, mas falhou ao habilitar a mailbox no Exchange. Erro: $($_.Exception.Message -replace "`r`n"," ")"
|
|
||||||
Write-Log -Level 'ERROR' -Message $errorMessage -TargetADServer $ExchangeADServer
|
|
||||||
Write-Host " [ERRO] Falha ao habilitar a mailbox no Exchange." -ForegroundColor Red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Processamento concluído."
|
|
||||||
Loading…
Reference in New Issue