Ansible + Windows: o básico, NTLM, DSC e mais.

Diego Morales
stonetech
Published in
9 min readJul 16, 2017

Eu passei os últimos anos trabalhando muito pouco quase nada com Windows. Meu coração está com o linux, mas eu não sou dado a fanatismos, e agora me encontro novamente trabalhando em um ambiente multiplataforma, com uso forte de Windows. Hora de aprender novos truques e tirar a poeira das minhas adormecidas Windows skills.

Parte disso está sendo testar como está o suporte de Windows no Ansible. Puppet, Salt (e acho que Chef também) tem um suporte mais avançado e/ou maduro a Windows, mas o Ansible tem avançado bastante recentemente. Nem tudo está muito bem documentado e difundido ainda, e nessa jornada eu descobri algumas jóias um pouco escondidas. Então, let’s blog.

Mantendo a filosofia agentless, o Ansible acessa o Windows remotamente usando recursos de acesso remoto do Powershell (Powershell Remoting). Para quem não sabe (eu não sabia), é possível abrir um console ou rodar comandos remotamente usando os comandos Enter-PSession e Invoke-Command no Powershell. Assim meio que parecido com um ssh, mas com uma sintaxe esquisita :D. Esse acesso remoto pode ser implementado de mais de uma maneira, usando RPC, WMI ou WS-Management, este último provido pelo serviço Windows Remote Management (WinRM). E é o WinRM que o Ansible utiliza.

Antes de botar na massa, só mais um comentário. O cenário normal e suportado é rodar o ansible em uma estação linux/unix. Para rodar o ansible a partir de uma estação Windows, a coisa complica. Cygwin não serve, Bash for Ubuntu no Windows 10 talvez, e eu já testei com sucesso uma solução usando docker. Mas isso é o assunto de outro post.

Mãos à obra

Leia a documentação oficial sobre Windows Support. Sério, leia, não é longa. Depois volte aqui e veja o meu resumo e o que funcionou pra mim.

(é, eu sei que você não vai ler, mas eu avisei)

Na máquina que roda o ansible (a control machine):

pip install "pywinrm>=0.1.1

Eu geralmente prefiro instalar o pacote da distro quando disponível, mas a versão pode acabar sendo antiga e não suportar alguma coisa, como por exemplo a autenticação NTLM que eu explico abaixo. Teste o pacote se quiser.

No Windows, rode este script mencionado na documentação. Ele tem alguns parâmetros que você pode ou não precisar. Veja a seção Windows System Prep na documentação. Eu li e entendi o script. Ele gera um certificado auto assinado, configura um listener SSL no WinRM na porta 5986 (5985 é a padrão sem SSL), e libera essa porta no firewall para as redes privadas.

Dependendo da versão do Windows, o trabalho pode ser maior:

PowerShell 3.0 or higher is needed for most provided Ansible modules for Windows, and is also required to run the above setup script. Note that PowerShell 3.0 is only supported on Windows 7 SP1, Windows Server 2008 SP1, and later releases of Windows.

Eles sugerem um script para fazer o upgrade do powershell, veja na doc.

No meu ambiente, estou fazendo essa preparação do windows já na imagem base de Windows que estou gerando (go packer!). Daí quando a máquina nasce já está pronta para acesso via ansible.

Configurando

Precisamos passar para o ansible alguns parâmetros para ele saber como conectar nas máquinas windows. Eu faço isso associando elas a um grupo “windows” no inventário e aí criando este arquivo de vars:

$ cat group_vars/windows 
---
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore

Com tudo que já falamos, acho que é bastante auto-explicativo. Claro que é necessário ter as credenciais também. Dá para fornecê-las iterativamente, se for seu caso, com os parâmetros normais -u e –ask-pass, ou manter em outro arquivo de variáveis criptografado com ansible vault:

$ ansible-vault edit group_vars/ansiblelab 
Vault password:
# opens in my $EDITOR:
---
ansible_user: myuser
ansible_password: mypassword

Bom, com isso, já dá para acessar a máquina utilizando o usuário da base local do Windows. Agora vamos ao AD…

Autenticando Windows com o Active Directory

Para autenticar no Windows, usando uma conta do AD (ao invés de uma conta local do servidor), o procedimento normal descrito na documentação do Ansible é utilizar Kerberos, tendo inclusive que colocar a control machine no domínio.

Arrrghh, que dor só de ouvir. E imagina se eu tiver mais de um domínio não relacionado (sem relação de confiança entre eles), vou ter que ter uma control machine para cada domínio?

Felizmente, não é bem assim. Para começar, é algo meio recente, mas dá para utilizar NTLM, que me parece uma alternativa muito mais simples e flexível.

Autenticando com NTLM

Basta adicionar essa opção aqui junto daquelas outras lá em cima:

ansible_winrm_transport: ntlm

Além disso, é preciso informar o domínio ao passar o usuário:

ansible_user: myuser@ANSIBLELAB.LOCAL
ansible_password: mypassword

Pronto, só isso! Note que eu usei o formato user@FULL.DNS.DOMAIN em vez do mais comum SHORTDOMAIN\user. Esse foi o jeito de funcionar da mesma com ntlm ou Kerberos, meu próximo teste.

Autenticando com Kerberos

Eu não sei se meu ambiente de teste (só nele eu testei com Kerberos) não representou bem a realidade, mas eu não precisei adicionar a control machine no domínio. No entanto, tem vários detalhes importantes:

  • Obviamente, a control machine precisa enxergar perfeitamente o domínio DNS associado ao AD em questão.
  • O relógio precisa estar sincronizado, isso é básico para qualquer ambiente Kerberos.
  • O registro reverso no DNS precisa estar correto. Isso me mordeu, demorei a me dar conta. Confesso que agora não tenho certeza se só os registros dos servidores windows ou da control machine também. Eu acertei todos eles.
  • O inventário precisa utilizar o nome do servidor Windows, e não o IP.

O método NTLM não precisa dessas coisas, mas cabe notar que são todas boas práticas a serem seguidas de qualquer forma.

Pacotes:

yum -y install python-devel krb5-devel gcc krb5-libs krb5-workstation
pip install kerberos requests_kerberos

Eu criei um domínio de teste, chamado ANSIBLELAB (ansiblelab.local), promovendo a Domain Controller o meu host de teste ansiblelab-w02. Não vou entrar em detalhes aqui sobre como fazer isso no Windows. Mas a configuração do Kerberos na minha control machine ficou assim:

# cat /etc/krb5.conf 
includedir /var/lib/sss/pubconf/krb5.include.d/
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
default_realm = ANSIBLELAB.LOCAL
default_ccache_name = KEYRING:persistent:%{uid}

[realms]
ANSIBLELAB.LOCAL = {
kdc = ansiblelab-w02.ansiblelab.local
kdc = ansiblelab-w02.ansiblelab.local
}

[domain_realm]
.ansiblelab.local = ANSIBLELAB.LOCAL

E nas minhas variáveis do ansible, eu setei para a WinRM usar kerberos:

ansible_winrm_transport: kerberos

Para que o ansible possa se autenticar via Kerberos, eu preciso rodar o kinit (para obter o TGT). Eu criei um usuário configurado como admin das máquinas windows, para testar.

kinit -U myuser@ANSIBLELAB.LOCAL

Ele pede a senha, e pronto, depois é só conectar com o ansible informando apenas o usuário e domínio (igual ao mostrado acima com o NTLM), não precisa mais colocar a senha.

Testando

Para começar, aquele teste com módulo ping do ansible, sempre útil para testar a conectividade básica antes de mais nada. Só que no caso do windows, vocês deve usar o módulo win_ping:

Se por acaso der um erro tal como “ssl: HTTPSConnectionPool(host=’w2′, port=5986): Max retries exceeded with url: /wsman (Caused by ConnectTimeoutError …”, então o listener SSL não foi habilitado na porta 5986 (veja lá em cima onde eu cito o script powershell que habilita isso).

Agora vamos rodar um playbook simples. Eu resolvi testar subir um virtual host no IIS, copiando os arquivos do site em um zip para o servidor. Veja aqui o playbook:

Destacando as principais tarefas nele:

  • Instala o IIS (win_feature, linha 8)
  • Cria o Website/VirtualHost (win_iis_website, linha 25)
  • Abre a porta do novo website no firewall (win_firewall_rule, linha 31)
  • Copia um arquivo zip com o conteúdo do site para um diretório de staging (win_copy, linha 45)
  • Descompacta o arquivo zip no diretório do site (win_unzip, linha 49)

Algumas outras tarefas menos interessantes estão ali também, para criar os diretórios envolvidos e para subir um arquivo index.html para o site default, em geral usando o win_file.

A execução fica assim:

Essa não foi a minha primeira execução, então algumas tarefas no início já estavam feitas (como a instalação do IIS), e ele não precisou fazer nada, marcando apenas como OK. O resto não tinha sido feito ainda e então ele executou e marcou como changed.

Esses dois warnings aparecem devido a algum bug inócuo introduzido recentemente. Nothing to worry about, eu achei dito na lista no Google Groups.

Mas tem um outro bug mais chato escondido aí. Ao executar novamente o playbook, a task do firewall me deu erro:

The rule exists but has different values. Arrgh. A vida não é um moranguinho, definitivamente. Há um issue aberto. Uma “solução” é setar force=true neste módulo, mas daí ele executa toda vez, marcando sempre como changed. Indesejável, para dizer o mínimo.

Módulos para Windows, e DSC

A lista de módulos para Windows já é grandinha, e tem crescido a cada versão. Vimos alguns no exemplo acima. A maioria é específico, com prefixo win_, mas alguns poucos funcionam tanto para linux quanto para windows, como o módulo script, que pode fazer upload e executar scripts powershell, bash, python, whatever. Tirado da doc:

Note:: There are a few other Ansible modules that don’t start with “win” that also function with Windows, including “fetch”, “slurp”, “raw”, and “setup” (which is how fact gathering works).

Além disso, existem outros ainda não oficiais, e os mais interessantes que eu achei foram os que linkei abaixo relativos ao DSC. Eu não testei eles ainda, mas é preciso comentar sobre eles pois o potencial é gigante.

O que é DSC? Desired State Configuration (DSC) é um conjunto de extensões ao Powershell para uma gerência de configuração de forma declarativa, nativa do Windows. Existe já um caminhão de resources DSC para realizar as mais variadas tarefas. E estão no GitHub. Tem módulo Puppet para DSC.

A partir do Powershell 5.0, é possível utilizar o DSC de forma mais isolada e pontual. O que permitiu um tal de trondhindenes criar um módulo ansible para executar DSC, win_dsc. Mas isso não é o melhor. Ele criou um gerador automático de módulos DSC pra ansible, com o qual ele conseguiu converter todo aquele caminhão módulos DSC já prontos, para módulos DSC ansible.

São muitos módulos. Eu ainda não sei se eles funcionam muito bem (testarei, não precisei deles ainda), mas caso positivo, o suporte do Ansible a Windows está catapultado a outro patamar.

Considerações finais e próximos passos

É preciso lembrar que nem tudo é uma beleza. O ansible não é tão maduro quanto caras mais antigos como Puppet ou Chef, e o suporte dele a Windows é especialmente novo. Haverão pedras no caminho. Eu não fiz muita coisa séria ainda e já encontrei algumas, como o bug do win_firewall_rule acima. Sinceramente, acho que qualquer destas alternativas ainda vai dar uma dor de cabeça extra ao lidar com Windows. Mas o cenário está evoluindo e o ansible é uma alternativa muito interessante nele.

Coisas que estou testando e que podem virar posts logo logo:

  • Instalar o novo Docker for Windows com containers nativos do Windows 2016, usando Ansible.
  • Examinar as alternativas (i.e. gambiarras) disponíveis para rodar o Ansible a partir de uma estação Windows.
  • Examinar alguns problemas que tive executando alguns módulos no Windows Server Core.

Outras ideias interessantes a serem exploradas:

  • O Active Directory tem um recurso de geração e distribuição automática de certificados (Auto Enrollment) que eu acredito ser uma boa solução para evitar os certificados auto-assinados. A geração “em massa” de certificados auto-assinados é uma nojeira, e um risco de segurança. Com certificados de uma CA confiada a segurança iria ficar muito mais séria.
  • Dado o passo acima, testar a autenticação via certificado SSL do cliente fica mais fácil. Pelo que vi superficialmente, acho que o serviço WinRM suporta isso e a pywinrm também.
  • Obter as credenciais de acesso ao Windows via HashiCorp Vault, ou talvez armazená-las no Ansible Tower ou quem sabe do Rundeck.
  • Testar aqueles módulos DSC mencionados acima, ver se funcionam bem.

Se alguém lendo isso tiver alguma experiência com os itens acima, please compartilhe

--

--

Diego Morales
stonetech

SRE Tech Lead na Stone. Doido por automação, DevOps, Agile, churrasco e corridas de aventura.