Skip to content

Ansible Windows Management using HTTPS and SSL

|

Managing your Windows machines and servers using Ansible is pretty easy as there are more than hundred modules available to handle your Windows operations. This is a series of articles and in this article you will learn how to connect Windows from Ansible using HTTPS and SSL certificate.

See other articles to learn how to manage windows using Ansible

Note: Most of the steps and commands are copied from the Ansible documentation but added a lot of explanations and additional details to support the process.

Ansible to Windows Authentication

You have multiple authentication options to connect and authenticate to your Windows machine from Ansible.

docs.ansible.com

You can see the basic authentication method here in this article – How to configure Windows for Ansible. In this article you will learn how to connect Windows from Ansible using HTTPS and SSL certificate.

Step 1. Generate SSL Certificate

You can create SSL Certificate from Linux or Windows (PowerShell) and in this guide we will explain how to generate SSL certificate from Linux (you can do this from your Ansible controlnode itself).

Set the username which we are going to use to access Windows machine (eg: ansible)

$ set WINUSERNAME="ansible"

Now we need to set some arguments for the openssl and we will create a openssl.conf file for the same. Create openssl.conf file as below.

$ cat > openssl.conf << EOL
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req_client]
extendedKeyUsage = clientAuth
subjectAltName = otherName:1.3.6.1.4.1.311.20.2.3;UTF8:[email protected]
EOL

To pass this openssl.conf we can set the OPENSSL_CONF environment variable so that openssl command will take this configuration file and use it during execution.

$ export OPENSSL_CONF=openssl.conf

Create SSL Certificate

$ openssl req -x509 -nodes \
  -days 3650 -newkey rsa:2048 \
  -out cert.pem \
  -outform PEM \
  -keyout cert_key.pem \
  -subj "/CN=$WINUSERNAME" \
  -extensions \
  v3_req_client

Generating a 2048 bit RSA private key
.......................................+++
......................................................................+++
writing new private key to 'cert_key.pem'
-----

Delete openssl.conf

$ rm openssl.conf

Step 2. Import a Certificate to the Certificate Store in Windows

Now you need to import the Certificate to the Windows machine which you want to connect from Ansible. Please note, below steps have to be executed on Windows machine.

First you need to copy cert.pem using any of the method (eg: WinSCP) to the Windows machine.

Import Issuing Certificate

Now you need to import the Certificate on Windows machine using PowerShell commands.

$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import("cert.pem")

$store_name = [System.Security.Cryptography.X509Certificates.StoreName]::Root
$store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
$store.Open("MaxAllowed")
$store.Add($cert)
$store.Close()

Import the client certificate public key. Since this is self-signed certificate both the issuing certificate and public key are the same.

$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import("cert.pem")

$store_name = [System.Security.Cryptography.X509Certificates.StoreName]::TrustedPeople
$store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
$store.Open("MaxAllowed")
$store.Add($cert)
$store.Close()

Step 3. Mapping a Certificate to an Account in Windows

Now you need to map the Certificate with a use account. It can be the Administrator account or any dedicated account like ansible which we have created for this specific purpose.

$username = "ansible"
$password = ConvertTo-SecureString -String "Welcome123" -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password

# This is the issuer thumbprint which in the case of a self generated cert
# is the public key thumbprint, additional logic may be required for other
# scenarios
$thumbprint = (Get-ChildItem -Path cert:\LocalMachine\root | Where-Object { $_.Subject -eq "CN=$username" }).Thumbprint

New-Item -Path WSMan:\localhost\ClientCertificate `
    -Subject "[email protected]" `
    -URI * `
    -Issuer $thumbprint `
    -Credential $credential `
    -Force

You will get some response as below.

   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\ClientCertificate

Type            Keys                                Name
----            ----                                ----
Container       {URI=*, Issuer=0E2C17459D7C82630... ClientCertificate_2070301166

Step 4. Enabling Certificate Authentication Option in Windows

PS > winrm set winrm/config/service/Auth '@{Certificate="true"}'
Auth
    Basic = true
    Kerberos = true
    Negotiate = true
    Certificate = true
    CredSSP = false
    CbtHardeningLevel = Relaxed

Step 5. Configure Variables in Ansible

I have configured my host variables as below in my inventory. You are free to configure these variable as per your configuration practices. Please note, I have disabled ansible_user and ansible_password to ensure that ansible is using SSL/HTTPS communication.


[windows]
win2019 ansible_host=192.168.99.105

[windows:vars]
ansible_connection=winrm

#ansible_user=ansible 
#ansible_password="MySecretPassword"

# enable HTTPS
ansible_winrm_scheme=https  
ansible_port=5986
ansible_winrm_transport=certificate
ansible_winrm_cert_pem=/Users/gini/.ssh/cert.pem
ansible_winrm_cert_key_pem=/Users/gini/.ssh/cert_key.pem
ansible_winrm_server_cert_validation=ignore 

Step 6. Connect from Ansible to Windows using HTTPS

Now we have configured SSL certificates and Ansible variables. Let’s test Ansible connection to Windows machine using a simple win_ping module. I am adding -vvv to enable high verbose mode to see and verify HTTP connection details as below.

$ ansible win2019 -m win_ping -vvv

ansible 2.9.0
  config file = /Users/gini/workarea/windows-ssl-demo/ansible.cfg
  configured module search path = ['/Users/gini/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/gini/python-venv/ansible-29/lib/python3.7/site-packages/ansible
  executable location = /Users/gini/python-venv/ansible-29/bin/ansible
  python version = 3.7.3 (default, Nov 15 2019, 04:04:52) [Clang 11.0.0 (clang-1100.0.33.16)]
Using /Users/gini/workarea/windows-ssl-demo/ansible.cfg as config file
host_list declined parsing /Users/gini/workarea/windows-ssl-demo/hosts as it did not pass its verify_file() method
script declined parsing /Users/gini/workarea/windows-ssl-demo/hosts as it did not pass its verify_file() method
auto declined parsing /Users/gini/workarea/windows-ssl-demo/hosts as it did not pass its verify_file() method
Parsed /Users/gini/workarea/windows-ssl-demo/hosts inventory source with ini plugin
META: ran handlers
Using module file /Users/gini/python-venv/ansible-29/lib/python3.7/site-packages/ansible/modules/windows/win_ping.ps1
Pipelining is enabled.
<192.168.99.105> ESTABLISH WINRM CONNECTION FOR USER: ansible on PORT 5986 TO 192.168.99.105
EXEC (via pipeline wrapper)
win2019 | SUCCESS => {
    "changed": false,
    "invocation": {
        "module_args": {
            "data": "pong"
        }
    },
    "ping": "pong"
}
META: ran handlers
META: ran handlers

Great ! All good and we have connected to Windows machine using Ansible over HTTPS. I want to try one more task using win_whoami module and see the result. There are so much details but let’s focus few items as highlighted below.

$ ansible win2019 -m win_whoami

win2019 | SUCCESS => {
    "account": {
        "account_name": "ansible",
        "domain_name": "WIN-OCEE554K769",
        "sid": "S-1-5-21-2652326372-650916474-2589809930-1000",
        "type": "User"
    },
    "authentication_package": "NTLM",
    "changed": false,
    "dns_domain_name": "",
    "groups": [
        .
        .
        .
        {
            "account_name": "Local account and member of Administrators group",
            "attributes": [
                "Mandatory",
                "Enabled by default",
                "Enabled"
            ],
            "domain_name": "NT AUTHORITY",
            "sid": "S-1-5-114",
            "type": "WellKnownGroup"
        },
        .
        .
    ],
    "impersonation_level": "SecurityAnonymous",
    "label": {
        "account_name": "High Mandatory Level",
        "domain_name": "Mandatory Label",
        "sid": "S-1-16-12288",
        "type": "Label"
    },
    "login_domain": "WIN-OCEE554K769",
    "login_time": "2021-09-29T21:54:55.8543746-07:00",
    "logon_id": 3606545,
    "logon_server": "WIN-OCEE554K769",
    "logon_type": "Network",
    "privileges": {
        "SeBackupPrivilege": "enabled-by-default",
        "SeChangeNotifyPrivilege": "enabled-by-default",
        "SeCreateGlobalPrivilege": "enabled-by-default",
        "SeCreatePagefilePrivilege": "enabled-by-default",
        "SeCreateSymbolicLinkPrivilege": "enabled-by-default",
        "SeDebugPrivilege": "enabled-by-default",
        "SeDelegateSessionUserImpersonatePrivilege": "enabled-by-default",
        "SeImpersonatePrivilege": "enabled-by-default",
        "SeIncreaseBasePriorityPrivilege": "enabled-by-default",
        "SeIncreaseQuotaPrivilege": "enabled-by-default",
        "SeIncreaseWorkingSetPrivilege": "enabled-by-default",
        "SeLoadDriverPrivilege": "enabled-by-default",
        "SeManageVolumePrivilege": "enabled-by-default",
        "SeProfileSingleProcessPrivilege": "enabled-by-default",
        "SeRemoteShutdownPrivilege": "enabled-by-default",
        "SeRestorePrivilege": "enabled-by-default",
        "SeSecurityPrivilege": "enabled-by-default",
        "SeShutdownPrivilege": "enabled-by-default",
        "SeSystemEnvironmentPrivilege": "enabled-by-default",
        "SeSystemProfilePrivilege": "enabled-by-default",
        "SeSystemtimePrivilege": "enabled-by-default",
        "SeTakeOwnershipPrivilege": "enabled-by-default",
        "SeTimeZonePrivilege": "enabled-by-default",
        "SeUndockPrivilege": "enabled-by-default"
    },
    "rights": [
        "SeNetworkLogonRight",
        "SeInteractiveLogonRight",
        "SeBatchLogonRight",
        "SeRemoteInteractiveLogonRight"
    ],
    "token_type": "TokenPrimary",
    "upn": "",
    "user_flags": []
}

Additional Steps

Once the HTTPS communication has been enabled and verified with WinRM, you may proceed with additional steps listed below to ensure you are using only secure communication between Ansible and your Windows machine. These are not mandatory but recommended as part of best practices.

Disable Username and Password if any

If you have configured any username or password for Windows machine, disable the same in your Ansible variable configurations; we do not need this anymore.

#ansible_user=ansible 
#ansible_password="MySecretPassword"

Disable unencrypted PowerShell Remoting

PS > winrm set winrm/config/service '@{AllowUnencrypted="false"}'

Service
    RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
    MaxConcurrentOperations = 4294967295
    MaxConcurrentOperationsPerUser = 1500
    EnumerationTimeoutms = 240000
    MaxConnections = 300
    MaxPacketRetrievalTimeSeconds = 120
    AllowUnencrypted = false
    Auth
        Basic = true
        Kerberos = true
        Negotiate = true
        Certificate = false
        CredSSP = false
        CbtHardeningLevel = Relaxed
    DefaultPorts
        HTTP = 5985
        HTTPS = 5986
    IPv4Filter = *
    IPv6Filter = *
    EnableCompatibilityHttpListener = false
    EnableCompatibilityHttpsListener = false
    CertificateThumbprint
    AllowRemoteAccess = true

Disable WinRM Basic Authentication

If you want to disable the WinRM Basic authentication (username & password), then disable the same.

PS > winrm set winrm/config/service/Auth '@{Basic="false"}'
Auth
    Basic = false
    Kerberos = true
    Negotiate = true
    Certificate = true
    CredSSP = false
    CbtHardeningLevel = Relaxed

Gineesh has worked as a Systems Engineer, Automation Specialist, and content author. His primary focus is on Ansible Automation, Containerisation (OpenShift & Kubernetes), and Infrastructure as Code (Terraform). (aka Gini Gangadharan - iamgini.com)

Gineesh has worked as a Systems Engineer, Automation Specialist, and content author. His primary focus is on Ansible Automation, Containerisation (OpenShift & Kubernetes), and Infrastructure as Code (Terraform). (aka Gini Gangadharan - iamgini.com)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.