Olá a todos, espero que estejam bem!
No artigo de hoje iremos abordar o tema de inserção de máquinas no domínio de forma automática utilizando scripts de inicialização, mas com um ponto bem interessante, as credenciais de autenticação ficarão salvas no OCI Vault para garantir a segurança.
Explicando o pouco alguns casos de uso, esse cenário pode ser interessante para você que precisa por exemplo trabalhar com Instance Pool, Autoscalling ou até mesmo o Serviço de Secure Desktop, para que, quando as instâncias são criadas, elas assim que iniciarem já realizem o processo de inserção de máquina no domínio.
Para atingir esse objetivo, iremos precisar:
- 1 Servidor de Active Directory
- Integração de DNS entre seu Active Directory e OCI, você pode usar esse artigo como referência
- OCI Vault usando o modelo de Software
- Oracle CLI
- Políticas de Segurança do IAM
- Dynamic Group
- 1 Servidor preparado com Sysprep e com custom image
Passo 1: Preparando as políticas de Segurança para a Instance Principal
Nessa etapa, iremos criar:
- 1 Dynamic Group apontando para o compartimento da nossas instâncias e
- 1 Política no mesmo compartimento da instância com permissões de Leitura do Serviço Vault
Para isso, navegue até Identity & Security > Compartments

Navegue até seu compartimento e colete o ocid, ele tem o formato:
ocid1.compartment.oc1…….

Com o ocid do compartimento salvo, navegue até Identity & Security > Domains

Acesse seu Domain e depois navegue até a opção Dynamic Group, clique em Create Dynamic Group

Nessa etapa, dê um nome e coloque o seguinte argumento, alterando o item em vermelho com o ocid do seu compartimento
ANY {instance.compartment.id = 'ocid1.compartment.oc1.....'}
Ficará dessa forma:

Após criar o Dynamic Group, iremos criar as políticas de segurança, elas no meu caso, serão criadas no mesmo compartimento que as instâncias e o Vault serão criados.
Para isso, navegue até Identity & Security > Polices

Depois clique em Create Policy

Crie uma política com o seguinte argumento
Allow dynamic-group <dg-grupo> to read vaults in compartment <compartimento>
Allow dynamic-group <dg-grupo> to read secret-family in compartment <compartimento>
Allow dynamic-group <dg-grupo> to use keys in compartment <compartimento>
Altere:
<dg-grupo> com o nome do seu Dynamic Group
<compartimento> com o nome do seu Compartimento
Importante:
Se você estiver trabalhando com domains, avalie utilizar o modelo de police abaixo, passando antes do nome do Dynamic Group o domínio que ele foi criado, exemplo:
Allow group <identity_domain_name>/<group_name> to <verb> <resource-type> in compartment <compartment_name>
Exemplo:

Com isso, finalizamos o Passo 1
Passo 2: Preparando a Imagem OCI com o Oracle CLI
Vamos utilizar uma instância inicial como nossa “Golden Image” que servirá de base para o recurso de ingresso automatico no Windows Active Directory quando ela iniciar.
Para isso, é necessário que já possua uma instancia iniciada em OCI e tenha acesso a ela via RDP.
Com o acesso através do RDP, agora iremos começar a preparar a instância
Primeiro baixe e instale o Python 3.11.9 (LINK), faça a instalação como Administrador e selecione os parâmetros abaixo
Para o artigo estou usando a versão 3.11.9, devido necessidade do pacote distutils e estar dentro das versões suportadas pra Oracle CLI
As versões suportadas para o Oracle CLI podem ser avaliadas nesse link: Versão Suportadas Oracle CLI

Fo fim da instalação, clique nessa opção, ele habilitar o suporte a caminhos longos, se não habilitar essa opção, durante a instalação do Oracle CLI poderá aparecer um problema.

Se você tiver problemas durante a instalação do Oracle CLI com o limite de tamanho do PATH, você pode abrir o Powershell como Administador, executar o comando abaixo e reiniciar a instancia para aplicar as configurações
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
Agora, iremos instalar o Oracle CLI, execute esse script no Powershell como Administrador
Configure a política de execução Remota
Set-ExecutionPolicy RemoteSigned
Se você estiver usando Windows Server 2012 ou 2016 force o Powershell a usar o TLS 1.2, versões superiores não é necessário.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Baixe o script do Oracle CLI, execute, no script está com a opção de aceitar os padrões (-AcceptAllDefaults) você pode remover esse argumento se quiser personalizar
Invoke-WebRequest https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.ps1 -OutFile install.ps1; ./install.ps1 -AcceptAllDefaults
Ao final, a mensagem de sucesso deve ser exibida

Após finalizado, defina a variável de autenticação para todos os usuários, execute no Powershell como Administrador.
[System.Environment]::SetEnvironmentVariable("OCI_CLI_AUTH", "instance_principal", [System.EnvironmentVariableTarget]::Machine)
Para validar, execute o comando:
oci os ns get --auth instance_principal
Ele deve retornar seu namepspace

Passo 3: Configurando o OCI Vault
Nessa etapa iremos configurar o OCI Vault e preparar nosso script para que após a instância iniciar ela consiga ingressar no Active Directory Automaticamente
Antes de tudo, tenha as credenciais do seu usuário de serviço que será responsável por realizar o ingresso de forma autônoma no Active Directory e também a OU Path que as instâncias serão adicionadas assim que fizerem Join no AD.
Feito isso, vá até Identity & Security > Vault

Clique em Create Vault, Selecione o mesmo compartimento que foi atribuida a política, depois dê um nome e por fim Create Vault no final da tela

Depois do Vault criado, crie uma Master Encryption Key

Defina o Compartimento, Modo de Proteção, Nome e tipo de Algoritmo

Após ela criada, navegue até Secret e depois Create Secret

Agora vamos criar 2 secrets. Ambas serão para o usuário de serviço, mas uma delas irá armazenar o nome de usuário e a outra a senha.
Iremos criar a primeira para o nome do usuário
Defina o Compartimento, Nome e Descrição, selecione a Chave criada anteriormente, depois a opção de Manual Secret Generation, tipo Plain-Text e por fim coloque o nome de usuário
As demais opções não iremos preencher e iremos clicar em Create Secret

Faça o mesmo processo para a senha de usuário, mas no campo de Secret Contents, coloque a senha do usuário.
Ao final, você terá as 2 secrets criadas

Abra cada Secret e colete o ocid delas, salve para uso posterior.

Passo 4 – Criando o Script Powershell para AutoJoin no AD
O primeiro passo é coletar o ocid do compartimento que esta presente suas Secrets
Tenha tambem já anotado o ocid de cada Secret criado no processo anterior
Para preencher o script você precisará ter em mãos as seguintes informações, altere os campos que estão no script de exemplo com suas informações.
# **Informações necessárias para preencher:**
# OCID do compartimento onde os segredos estão armazenados
$compartmentId = "<OCID do compartimento>" # Exemplo: ocid1.compartment.oc1..aaaaaaaabcxyz12345
# OCIDs dos segredos para o nome de usuário e senha
$usuarioSecretId = "<OCID do segredo do nome de usuário>" # Exemplo: ocid1.vaultsecret.oc1.iad.aaaaaaaabcxyz12345
$senhaSecretId = "<OCID do segredo da senha>" # Exemplo: ocid1.vaultsecret.oc1.iad.aaaaaaaabcxyz67890
# **Informações do Active Directory**
$Dominio = "<Nome do domínio>" # Exemplo: corp.local
$ouPath = "<Caminho da Unidade Organizacional>" # Exemplo: OU=Servers,DC=corp,DC=local
O script completo de exemplo está abaixo e se precisar fazendo download, segue link: Download Script
Salve esse esse script, após alterar em C:\temp

#ps1_sysnative
# Configuração inicial do log
$logFile = "C:\temp\adjoin_log.txt"
if (-not (Test-Path "C:\temp")) {
New-Item -ItemType Directory -Path "C:\temp" | Out-Null
}
function Write-Log {
param (
[string]$Message,
[string]$Type = "INFO" # INFO, ERROR, SUCCESS
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp [$Type] $Message" | Out-File -FilePath $logFile -Append
if ($Type -eq "ERROR") {
Write-Host $Message -ForegroundColor Red
} elseif ($Type -eq "SUCCESS") {
Write-Host $Message -ForegroundColor Green
} else {
Write-Host $Message -ForegroundColor Cyan
}
}
# Início do script
Write-Log "Iniciando o script de registro no Active Directory..."
# Configuração da autenticação no OCI CLI
$env:OCI_CLI_AUTH = "instance_principal" # Autenticação como Instance Principal
# **Informações necessárias para preencher:**
# OCID do compartimento onde os segredos estão armazenados
$compartmentId = "<OCID do compartimento>" # Exemplo: ocid1.compartment.oc1..aaaaaaaabcxyz12345
# OCIDs dos segredos para o nome de usuário e senha
$usuarioSecretId = "<OCID do segredo do nome de usuário>" # Exemplo: ocid1.vaultsecret.oc1.iad.aaaaaaaabcxyz12345
$senhaSecretId = "<OCID do segredo da senha>" # Exemplo: ocid1.vaultsecret.oc1.iad.aaaaaaaabcxyz67890
# **Informações do Active Directory**
$Dominio = "<Nome do domínio>" # Exemplo: corp.local
$ouPath = "<Caminho da Unidade Organizacional>" # Exemplo: OU=Servers,DC=corp,DC=local
# Listar os segredos no compartimento para validação
Write-Log "Listando os segredos no compartimento..."
$secretsList = & oci vault secret list --compartment-id $compartmentId --query "data[].id" --raw-output
if (-not $secretsList) {
Write-Log "Nenhum segredo encontrado no compartimento $compartmentId." "ERROR"
exit 1
}
# Recuperar os segredos (Base64) para nome de usuário e senha
Write-Log "Recuperando segredos..."
try {
$usuarioBase64 = & oci secrets secret-bundle get --secret-id $usuarioSecretId --query "data.\`"secret-bundle-content\`".content" --raw-output
$senhaBase64 = & oci secrets secret-bundle get --secret-id $senhaSecretId --query "data.\`"secret-bundle-content\`".content" --raw-output
} catch {
Write-Log "Erro ao recuperar os segredos: $_" "ERROR"
exit 1
}
# Validar se os segredos foram retornados
if (-not $usuarioBase64 -or -not $senhaBase64) {
Write-Log "Erro: Um ou mais segredos retornaram valores nulos ou inválidos." "ERROR"
exit 1
}
# Decodificar os segredos do formato Base64
Write-Log "Decodificando segredos..."
try {
$Usuario = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($usuarioBase64))
$Senha = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($senhaBase64))
Write-Log "Usuário decodificado: $Usuario"
} catch {
Write-Log "Erro ao decodificar os segredos: $_" "ERROR"
exit 1
}
# Convertendo a senha para formato seguro
Write-Log "Convertendo senha para formato seguro..."
try {
$SenhaSecureString = ConvertTo-SecureString $Senha -AsPlainText -Force
} catch {
Write-Log "Erro ao converter senha para formato seguro: $_" "ERROR"
exit 1
}
# Criando o objeto de credencial
$Credential = New-Object System.Management.Automation.PSCredential($Usuario, $SenhaSecureString)
# Registrando a instância no Active Directory
Write-Log "Iniciando o registro no Active Directory..."
try {
Add-Computer -DomainName $Dominio -Credential $Credential -OUPath $ouPath
Write-Log "Instância registrada com sucesso no Active Directory." "SUCCESS"
} catch {
Write-Log "Erro ao registrar a instância no Active Directory: $_" "ERROR"
exit 1
}
Nessa pasta, também será salvo o log de execução quando inserido no Active Directory e para debug de possíveis erros caso ocorram.
Passo 5 – Preparando para o Sysprep e Criação da Custom Image
Chegamos a última parte do artigo!
Vamos configurar uma tarefa agendada no Windows que irá executar nosso script de Adjoin quando a máquina ligar pela primeira vez e após executar irá apagar o arquivo.
Aqui temos um ponto importante. Como vou usar a conta opc, edite C:\Program Files\bmcs\imageType.json
e altere a definição imageType
para custom
, pois como vamos colocar a senha para execução e temos que ter a possibilidade de executar como Instance Principal, precisa ser uma conta que tenha possibilidade de fazer chamadas via instance principal
Para isso, navegue até o Agendador de Tarefas do Windows e crie uma tarefa.

Dê um nome para tarefa, altere para system e habilite a opção de executar com privilégios elevados, selecione a conta opc.

Em Triggers (disparadores) selecione a opção Ao Iniciar o Sistema, também defina uma data de expiração, de preferência longa. Isso será necessário pois ao fim dela, iremos adicionar a opção de deletar a tarefa imediatamente após a execução, e para isso é obrigatório configurar uma data de expiração.

Em Actions (Ações) selecione Start a Program (Iniciar um Programa), e digite na caixa powershell.exe
Em argumentos, coloque o seguinte parâmetro
-File C:\temp\domainjoin.ps1
Esse é o caminho do seu script DomainJoin

Adicione uma segunda condição agora, para que, após executar o arquivo ele delete o arquivo do diretório, crie uma nova condição com o seguinte argumento
Remove-Item -Path "C:\temp\domainjoin.ps1" -Force

Adicione uma terceira condição para que após ele execute o Join no AD, apague o arquivo ele reinicie a instância, para isso crie uma ação, recomendo 2 minutos de espera para reiniciar devido o tempo necessário para fazer o Adjoin.
shutdown
/r /t 120

Deverá ficar desse jeito:

Por fim, iremos agora na aba Settings e definir que essa tarefa de execução única seja deletada após finalizar

Clique em OK, digite a senha do usuário opc

Agora vamos agora realizar os procedimentos para Sysprep. Como base iremos utilizar a documentação oficial para criar uma Imagem Generalizada
Baixe o arquivo: oracle-cloud_windows-server_generalize_2022-08-24.SED.EXE na pasta C:\Windows\Panther
Execute como administrador na pasta para extrair os arquivos


Ao extrair, aquivos de configuração padrão como
- Generalize.cmd
- Specialize.cmd
- unattend.xml
- Post-Generalize.ps1
Serão adicionados a pasta. O arquivo unattend.xml tem alguns parametros de configuração, como nome de host. Se não editar ele irá iniciar as instâncias como a nomenclatura ORACLE-XXXX, você pode personalizar o arquivo para ter as informações pertinentes ao seu ambiente.
No meu caso, para pegar o hostname aplicado em OCI eu alterei a linha do unattend.xml
De:
<ComputerName></ComputerName>
Para:
<ComputerName>*</ComputerName>
Depois de realizar a validação e ajuste do arquivo unattend.xml, execute o arquivo Generalize.cmd como administrador


Quando terminar a instância irá desligar via Sistema Operacional. No console do OCI ela ainda ficará operante
Aguarde aproximadamente 2 minutos depois que ela desligar após o processo do Sysprep e interrompa ela no console através do Botão Stop > Force Stop

Quando a instância estiver interrompida no Console, crie uma Custom Image, essa será sua Golden Image que irá realizar o Join no Active Directory quando for criada automaticamente


Agora vamos validar, irei criar uma instância nova baseada na Custom Image, e após ela iniciar já temos o ingresso automático no Active Directory
Lembre-se de criar a instância sempre nos compartimentos que você criou o Dynamic Group, se necessário atualize as políticas de Dynamic Group e Policies OCI.
A validação das credenciais no Vault sempre serão via Instance Principal



Espero que esse artigo seja útil e auxilie nas tarefas do dia a dia.
Qualquer dúvida ou sugestões fiquem a vontade para enviar mensagens.
Obrigado.