Connect to Windows host using domain account from ansible controller not joined into AD domain

The discussion we had previously is only useful to manage a windows PC with local username/password. In order to manage a domain windows PC we have to install kerberos module for Ansible. I will give the guide regarding the setup of ansible controller to manage a domain windows PC while ansible controller itself is not within the domain.

1, Even through ansible controller is not necessary to join the domain, it is necessary that  machine of ansible controller can reach domain DNS and NTP services.  We can configure NTP server as the same domain NTP server if possible.

To get NTP used with windows host, enter “w32tm /query /peers” in cmd of window.

2, Join domain network, acquired IP address, WINS, DNS etc från domain network, this can happens automatically via DHCP when connected ansible controller to the domained network.

3, Install kerberos for ansible (example for Mac OS X)

pip install request kerberos
pip install pywinrm[kerberos]

4, Configure kerberos

edit etc/krb5.conf file, if krb5.conf is not existed, create one:

below is an example of krb5.conf file:

mac-c02t6npagtfj:etc grayin$ vi krb5.conf

        krb5_run_aklog = 1
        krb5_aklog_path = /usr/local/krb5/bin/aklog
        default_lifetime = 25h
        telnet = {
                autologin = 1
        xdm = {
               retain_ccache = 1
                afs_retain_token = 1
       pam = {
                ticket_lifetime = 90000
                renew_lifetime = 90000
                forwardable = true
        forwardable = true
        default_realm = LFAD.LFNET.SE
        DOMAIN.COM = {
                kdc = E00DC0008.DOMAIN.COM
                admin_server = E00DC0008.DOMAIN.COM
                default_domain =
[domain_realm] = DOMAIN.COM = DOMAIN.COM
        profile = /etc/kdc.conf
        default = SYSLOG:INFO:LOCAL6


[realms] and [domain_realm] are the area where new domain information need to be added. Using echo %username% will allow you to identify the authenticating domain controller.If you just desire to identify which domain controller the user retrieved group policies from you can type gpresult /r.

5, Test kerberos connection

#kinit user@MY.DOMAIN.COM

#To see what tickets if any you have acquired, use the command klist


mac-c02t6npagtfj:ansible_test grayin$ kinit username@DOMAIN.COM
username@DOMAIN.COM's password: 

'mac-c02t6npagtfj:ansible_test grayin$ klist
Credentials cache: API:FA05485A-8E6B-45F8-9526-D4B4EEDA5D1D
        Principal: username@DOMAIN.COM
  Issued                Expires               Principal
Mar  8 14:49:48 2018  Mar  9 00:49:44 2018  krbtgt/DOMAIN.COM@DOMAIN.COM

Once you have a valid ticket, you can check to ensure that everything is working as expected from command line. To test this, make sure that your inventory looks like the following:


ansible_user = username@DOMAIN.COM
ansible_connection = winrm
ansible_port = 5986
# The following is necessary for Python 2.7.9+ when using default WinRM self-signed certificates:
ansible_winrm_server_cert_validation = ignore

Especially “ansible_winrm_transport” can be changed to ssl if you want to authenticate with a local account of windows PC.  When connecting to windows host there are several authentication options that can be used, refer to here

Option Local Accounts Active Directory Accounts Credential Delegation
Basic, ssl Yes No No
Certificate Yes No No
Kerberos No Yes Yes
NTLM Yes Yes No
CredSSP Yes Yes

6, Make sure that managed windows pc is listening on 5986 and the firewall on PC is turned off or traffic to/from port 5986 is allowed. To check the state of configuration settings, type winrm get winrm/config. To check if tcp 5986 is listening and no firewall blocking, type telnet win01 5986 from ansible controller.

It can be a very headache to turn off windows defenders when “off” button is grey i no matter windows security center or windows firewall&network, or windows defender. the most efficient way for me is to go directly to regedit and change value there.

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender find “start” value from domain/private/public separately and set the value from 1 to 0.

Of course you can allow winrm https 5986 application from firewall allow application setup. But this option is grey too in my test environment. Then we can try to change configuration from pgedit.msc, to use group policy editor to create incoming traffic rules for winrm:

Go to gpedit.msc, computer configuration ->windows settings ->security settings -> windows firewall with advanced security -> inbound rules add rules


In my test I got stuck in “password incorrect” message when set correct password in the inventory sensible_password=password, even though I am pretty sure that I have typed the correct password.

mac-c02t6npagtfj:ansible_test grayin$ ansible-playbook -i host main.yaml
PLAY [Hit a Specific Host on the Server] *************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
fatal: [D75C0004.LFAD.LFNET.SE]: UNREACHABLE! => {"changed": false, "msg": "Kerberos auth failure: a940bf@LFAD.LFNET.SE's password: \nkinit: Password incorrect", "unreachable": true}
to retry, use: --limit @/Users/grayin/ansible_test/main.retry

Here is the discussion regarding this problem. It seems that  winrm module work if you get a kerberos token via kinit before executing ansible, even if the host isn’t joined to the domain. It fails though if you try to rely on the ansible_user/ansible_password combination.

I also encountered another problem that I must have account which owns administrative right for window computer in order to get connection successfully, Otherwise ansible kerberos connection will get error for either  time_out or credential refused or need paaeven though I have a valid kerberos toke listed by klist.



Install multiple versions of python i OS X

If you need install multi python versions in the host, and use different version of python for different application, it is very useful to use pyenv to manage different version installed in the host. For example, I have python 3.6 and 2.7 installed in my Mac, where python –version shows default version is 3.6

mac-c02t6npagtfj:ansible_test grayin$ python –version

Python 3.6.4

But I want to change my default python to version 2.7 in my Mac, and use pip in package 2,7 to install new packages in python2.7.

What I have done is to install pyenv first, then use pyenv to reinstall version 2,7 and 3.6, after that, change version of python used in the host:

$ brew update
$ brew install pyenv

To enable pyenv in your Bash shell, you need to run:

$ eval "$(pyenv init -)"
After this, we can use pyenv to reinstall python
$ pyenv install 2.7.14
$ pyenv install 3.6.4


And you can switch between python versions with the command:

$ pyenv global 2.7.14

Also you can set a python version for the current directory with:

$ pyenv local 3.6.4
mac-c02t6npagtfj:ansible_test grayin$ pyenv versions
* 2.7.14 (set by /Users/grayin/.pyenv/version)
After this:
mac-c02t6npagtfj:ansible_test grayin$ python --version
Python 2.7.14
mac-c02t6npagtfj:ansible_test grayin$ pip --version
pip 9.0.1 from /Users/grayin/.pyenv/versions/2.7.14/lib/python2.7/site-packages (python 2.7)

Even pip as default is attached to python2.7 now.

Pyenv can be helpful to avoid home-brew lost track of python

Sometime people may feel very confused that he/she has installed a package already but when importing it in the script, it would show that package is not installed or can not find the module.  This is because that when pip is attached to version 2.7, all package installed using pip will be installed for version 2.7, when pip is attached to version 3.6, all package installed using pip will be for version 3.6.  If version 2.7 is used but actually pip is attached to version 3.6, package will be installed in version 3.6.

Another alternative to address this issue is to use different /path/pip to install the package for the different version of python. Anyhow I have not tested this option.

Some also recommend the easier way for using pip in different version installation:

$ pip2.6 install otherpackage
$ pip2.7 install mybarpackage

However, this does not work for me:

mac-c02t6npagtfj:ansible_test grayin$ pip2.7 --version
pip 9.0.1 from /Users/grayin/.pyenv/versions/2.7.14/lib/python2.7/site-packages (python 2.7)
mac-c02t6npagtfj:ansible_test grayin$ pip3.6 --version
pyenv: pip3.6: command not found
The `pip3.6' command exists in these Python versions:
mac-c02t6npagtfj:ansible_test grayin$ pip3.6.4 --version
-bash: pip3.6.4: command not found


setup An Ansible Test Lab For Windows Managed hosts

I use Mac, and have setup a virtual window 10 host via VMware fusion. The target of the lab is to use ansible to restart windows 10 virtual host, after this, I have created a playbook to finish several tasks in this virtual host: test connection using win_ping, restart host using win_reboot, and visit a website using win_uri. The lab can be extended by adding new task into the playbook:

1, create host file (host inventory) in the folder /etc/ansible/hosts

#mkdir /etc/ansible

#touch /etc/ansible/hosts

#vi /etc/ansible/hosts

add IP address of the virtual host that will be connected remotely

2, create inventory file to save the other attributes. for example, create “windows.yml” to save username/password, the connection method used to manage windows hosts:

$ pwd



:group_vars grayin$ vi windows.yml

#It is suggested that these be encrypted with ansible-vault:

# ansible-vault edit group_vars/windows.yml

ansible_ssh_user: username

ansible_ssh_pass: password

ansible_ssh_port: 5986

ansible_connection: winrm

# The following is necessary for Python 2.7.9+ (or any older Python that has backported SSLContext, eg, Python 2.7.5 on RHEL7) when using default WinRM self-signed certificates:

ansible_winrm_server_cert_validation: ignore

Normally ansible uses ssh to communicate managed hosts, that means in order to manage windows host, windows need have ssh server enabled first. Here is a indication about the ways to enable ssh on windows 10

However ansible recommended to use Winrm to remotely manage windows hosts. Here is ansible document about winrm setup. In short, I did the following in my virtual window 10 machine, and then set ansbile_connection attribute to “winrm” in my above windows.yml file.

I did download file “ConfigureRemotingForAnsible.ps1” to the windows hosts. and run the following command in powershell.

$url = ""
$file = "$env:SystemDrive\temp\ConfigureRemotingForAnsible.ps1"

(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)

powershell.exe -ExecutionPolicy ByPass -File $file

After this, I got 2 listener for http port 5895 and https 5896.

winrm enumerate winrm/config/Listener

This will output something like the following:

    Address = *
    Transport = HTTP
    Port = 5985
    Enabled = true
    URLPrefix = wsman
    ListeningOn =,,, ::1, fe80::5efe:, fe80::5efe:, fe80::
ffff:ffff:fffe%2, fe80::203d:7d97:c2ed:ec78%3, fe80::e8ea:d765:2c69:7756%7

    Address = *
    Transport = HTTPS
    Port = 5986
    Hostname = SERVER2016
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint = E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE
    ListeningOn =,,, ::1, fe80::5efe:, fe80::5efe:, fe80::
ffff:ffff:fffe%2, fe80::203d:7d97:c2ed:ec78%3, fe80::e8ea:d765:2c69:7756%7

3, run testto window 10 using winrm connection. I use win_ping module, which is doing the similar thing as module “ping” towards linux/unix server

:ansible_test grayin$ ansible windows -i host -m win_ping | SUCCESS => {

    “changed”: false, 

    “failed”: false, 

    “ping”: “pong”


4, test restart of windows 10 host by using module “win_reboot”

mac-c02t6npagtfj:ansible_test grayin$ ansible windows -i host -m win_reboot | SUCCESS => {

    “changed”: true, 

    “elapsed”: 162, 

    “failed”: false, 

    “rebooted”: true


Now we have seen that virtual host is reboot and up again. Below I will create a playbook to implement several tasks one by one

5,create a file named as “test.yaml”

mac-c02t6npagtfj:ansible_test grayin$ vi test.yaml

- name: Hit a Specific Host on the Server



    user: "username"

    password: "passoword"


 - name: ping host ok


  - name: restart the host


   - name: get url


      url: ""

      method: GET

  - name: Perform a HEAD on an Endpoint



      method: HEAD

6, run playbook and get the following result:

mac-c02t6npagtfj:ansible_test grayin$ ansible-playbook -i group_vars/windows.yml -i host task.yaml

PLAY [Hit a Specific Host on the Server] **********************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************

ok: []

TASK [ping host ok] *******************************************************************************************************************************************

ok: []

TASK [restart the host] ***************************************************************************************************************************************

changed: []

TASK [get url] ************************************************************************************************************************************************

ok: []

TASK [Perform a HEAD on an Endpoint] **************************************************************************************************************************

ok: []

PLAY RECAP ****************************************************************************************************************************************************              : ok=5    changed=1    unreachable=0    failed=0

7, extend the playbook

We can add more management tasks into the playbook, ansible documents has listed all the available modules that can be used to manage windows hosts. More info can be found here.

Here is the blog regarding “setup An Ansible Test Lab For Windows Managed Nodes & Custom Windows Modules”

Install ansible in mac os

Ansible is mainly used for automize Linux/windows servers provisioning and operation, however from version 2.1 there is support module for network related devices.

In order to test it I have first install ansible in my mac:

There are several ways to install ansible, but the mostly common used on mac is homebrew an pip. Here is the comparision of both installation ways:

"pip is a packager for the python world – you should only ever be able to install python-things with it; homebrew is a package manager targetted at OSX; it doesn’t impose any restrictions onto what software you can install with it – since python is a subset of software.

installing things with brew will install them into /usr/local/;

installing things with pip will fetch packages from the Python Package Index, and it will install them in a place where your python interpreter will find them: either into your home directory (e.g. ~/.local/lib/python2.7/site-packages/) or in some global search-path of your python interpreter (e.g. /usr/local/lib/python2.7/dist-packages/)”

We will just explore the way to install ansible with homebrew:

1, install Xcode (C compiler) in order to use python
xcode-select –install

2, Install python using homebrew

brew install python


brew install python3

Actually, step 1 and 2 can be skipped because all new Mac OS X has python 2.7 installed already.

3, brew install ansible

After the installation we can find ansible is installed under /usr/local/bin/

mac-c02t6npagtfj:bin grayin$ ls ansible*

ansible ansible-doc ansible-pull

ansible-config ansible-galaxy ansible-vault

ansible-connection ansible-inventory

ansible-console ansible-playbook

notes: do “brew update” first before the installation to avoid any unexpected errors

Some concept used in SDN


The Network Configuration Protocol (NETCONF) defined in this document provides mechanisms to install, manipulate, and delete the configuration of network devices. It uses an Extensible Markup Language (XML)-based data encoding for the configuration data as well as the protocol messages. The NETCONF protocol operations are realized as remote procedure calls (RPCs).


YANG is a data modeling language used to model configuration and state data manipulated by the Network Configuration Protocol (NETCONF), NETCONF remote procedure calls, and NETCONF notifications.


describes a REST-like protocol that provides a programmatic interface over HTTP for accessing data defined in YANG, using the datastores defined in NETCONF.


Copied from internet

“BGP-LS is an extension to Border Gateway Protocol (BGP) for distributing the network’s link-state (LS) topology model to external entities, such as the SDN controller. It has received a lot of attention because many SDN apps need this model.

The network’s link-state topology model consists of nodes (typically, but not limited to, routers) and links that connect these routers together. For each link, a set of attributes is also contained. These may include interface addresses, various metrics, and each link’s total and available bandwidth. This topology model is distributed among routers using one of the two prominent Interior Gateway Protocols (IGPs): ISIS (Intermediate System to Intermediate System) and OSPF (Open Shortest Path First) protocols.

The detailed link-state models of these two protocols are not identical. As a result, BGP-LS defines its own more abstract topology model and defines how to map these IGP models to its own model. As the network topology is discovered by the IGPs, the changes are reflected in the BGP-LS model as well and are also distributed using BGP-LS messages to any interested party, such as SDN controllers and apps. Note that the network devices themselves are not interested in learning the network topology this way, as they already participate in IGP and learn it firsthand.”


The Cisco Application Policy Infrastructure Controller (Cisco APIC) is the unifying point of automation and management for the Application Centric Infrastructure (ACI) fabric.

It use policy agent to translate polices into instructions.


The EEM(Embedded Event manager is a software component of cisco IOS, XR, and NX-OS makes life easier for administrators by tracking and classifying events that take place on a router and providing notification options for those events. EEM allows you to automate tasks, perform minor enhancements and create workarounds.
There are two independent pieces: Applets and Scripting
-> Applets are a collection of CLI commands
-> Scripts are actions coded up in TCL(interpreter language)

EEM uses event detectors and actions to provide notifications of those events:

EEM detectors can be:
1) SNMP:-Monitoring SNMP objects.
2) Syslog:-Responds to various syslog messages, allowing for matching on regular expressions.
3) Counter: Monitoring and responding to interface counter when cross threshold settings.
4) CLI events: Screening CLI input for a regular expression match.
5) None: This event detector is use to test EEM script/applet using “event manager run” command.
6) Timers :(Countdown, watchdog and CRON)
7) IP SLA and Netflows events.


.cloginrc is a file that contains login information for each devices that will be accessed via clogin. .cloginrc is normally located under /home/username.

Below is an example of .cloginrc file:

# comments are cool, as is whitespace
# clogin supports a number of add directives:
# password
# user
# userprompt
# userpassword
# passprompt
# method
# noenable
# enauser
# enableprompt
# autoenable
# cyphertype
# identity
# Details on each of these follows. Also see cloginrc(5).
# add password <router name glob> <vty passwd> <enable passwd>
# add user <router name glob> <username>
# The default user is $USER (i.e.: the user running clogin).
# add userprompt <router name glob> <username prompt>
# What the router prints to prompt for the username.
# Default: {“(Username|login|user name):”}
# add userpassword <router name glob> <user password>
# The password for user if different than the password set
# using ‘add password’.
# add passprompt <router name glob> <password prompt>
# What the router prints to prompt for the password.
# Default: {“(\[Pp]assword|passwd):”}
# add method <router name glob> {ssh} […]
# Defines, in order, which connection method(s) to use for a device
# from the set {ssh,telnet,rsh}. e.g.: add method * {ssh} {telnet} {rsh}
# will attempt ssh connection first. if ssh fails with connection
# refused (i.e.: not due to authentication failure), then try telnet,
# then rsh.
# Default: {telnet} {ssh}
# add noenable <router name glob> <1>
# equivalent of -noenable on the cmd line to not enable at login.
# add enableprompt <router name glob> <enable prompt>
# What the router prints to prompt for the enable password.
# Default: {“\[Pp]assword:”}
# add enauser <router name glob> <username>
# This is only needed if enable asks for a username and this
# username is different from what user is set to.
# add autoenable <router name glob> <1/0>
# This is used if you are automatically enabled by the login process.
# add cyphertype <router name glob> <ssh encryption type>
# Default is 3des.
# set ssh encryption type, dflt: 3des
# Newer SG300 don’t support cbc, add aes256-ctr
add cyphertype * {aes256-cbc,aes256-ctr}
# add identity <router name glob> <path to ssh identity file>
# Default is your default ssh identity.
# include <file>
# include a secondary .cloginrc file
# Note: The first match for a hostname takes precedence.

#add password sl-bb*-dc cow24
#add password sl-gw*-dc geeks
#add password sl* hank dog
#add password at* pete cow
#add password sdn* mujahid horse
#add password icm* peter
#add password * anything
#add user sl-gw*-dc twit
#add user sdn* sdn_auto
#add user sdn-bb* ops_eng
#add user * $env(USER)

# customer x
# these routers ask for a username and password. we automatically get
# enable access after successful authentication.
#add user * roger
#add password * {doger}
#add autoenable * 1

# customer y
# this is the normal cisco login. a password followed by and enable password.
# try ssh first, then rlogin.
#add password * {vector} {victor}
#add method * ssh rlogin

# customer z; they use ssh only.
#add user * shirley
#add password * {jive} {surely}
#add method * ssh

# the route-server’s do not provide enable access. cmdline -noenable
# equivalent.
#add noenable route-server* 1

# all our routers, i.e.: everything else
#add password * {clearance} {clarence}

# set ssh encryption type, dflt: 3des
#add cyphertype * {3des}

# set the username prompt to “router login:”
#add userprompt * {“router login:”}

# ssh identity for a juniper; used with jlogin
#add identity $env(HOME)/.ssh/juniper

# riverstone / enterasys / cabletron (rivlogin) example
# these boxes are ‘back-to-front’ from cisco (i.e., ask
# for vty password always, then tac+/radius if configured).
# vty password and last resort (enable) password for rivlogin
#add password rs3000 {vtypass} {lastresort}
# if using tac+ or radius login, include these lines
#add user rs3000 {monster}
#add userpassword rs3000 {scary}

# include non-ssh-capable switches (i.e. 3500XL, 2900XL and sth1-core1a)
include /local/rancid/.cloginrc-switchesnonsshcapable

## Firewalls, only ssh is allowed

add method firewallname* ssh
add user firewallname* username

add password firewallname* {password}


Using clogin to automate some boring stuff

In some cases we need do some boring stuff, for example, checking every devices to document hardware type, serier number; to backup configuraiton; doing the same tiny changes on each network devices. It is time-consuming too if you have massive similar devices that you have to login and check one by one. This type of works can be automated by using clogin plus some simple scripts.

According to the maual,  clogin is an expect(1) script to automate the process of logging into a Cisco router, Catalyst switch, Extreme switch, Juniper ERX/E-series, Procket Networks, or Redback router. There are complementary scripts for Alteon, Avocent (Cyclades), Bay Networks (nortel), ADC-kentrox EZ-T3 mux, Foundry, HP Procurve switches and Cisco AGMs, Hitachi routers, Juniper Networks, MRV optical switch, Mikrotik routers, Netscreen firewalls, Netscaler, Riverstone, Netopia, Cisco WLCs and Xirrus arrays.

clogin reads the .cloginrc file for its configuration, then connects and logs into each of the routers specified on the command line in the order listed. Command-line options exist to override some of the directives found in the .cloginrc configuration file.

Below are simple examples of how clogin works:

Example 1

We want to login to each devices and check its serier number.  Let us assume that we have 20 devices, all of them are Juniper EX switches. Indead of logining and checking every devices, we can do the following script in Linux:

  1. list all devices hostname into a file “switches”
  2.  run the following script: for i in `cat /tmp/switches`; do /local/rancid/bin/clogin -autoenable -c “show virtual-chassis” $i >> /tmp/switchessn; done
  3. trim “switchessn” file to remove noneed text if necessary. now we have a file that contain all serier number and hardware type of switches

Example 2

If we need find our root bridge for each vlans in a layer 2 networks. Below is the script that I learned:

  1. Run the the first step to get the bridge ID´s of all the switches listed int eh file swtiches.txt
    for i in `cat /tmp/switches.txt`; do /local/rancid/bin/clogin -autoenable -c "sh spanning-tree bridge \n\n end" $i > /tmp/$i; done
  2. Purge the documents from step 1 to only contan the isolated bridge-id for each switch
    for b in `cat /tmp/switches.txt`; do egrep -o -m 1 "[[:space:]][[:alnum:]]{1,4}\.[[:alnum:]]{1,4}\.[[:alnum:]]{1,4}[[:space:]]" /tmp/170390/$b > /tmp/access-bridge-id/bridge-id_$b.txt; done
  3. As step 3 to collect the “spanning-tree root-id” table from each swith in the switches.txt document
    for v in `cat /tmp/switches.txt`; do /local/rancid/bin/clogin -autoenable -c "sh spanning-tree root \n\n end" $v > /tmp/root-id/root-id_$v; done
  4. Collect all file names containing the access-switch bridge-id´s to one file for parsing during the last step:
    printf "%b\n" /tmp/170390/access-bridge-id/* > /tmp/170390/access-bridge-id/access-bridges.txt
  5. Finally extract all the entries from the previous steps to find out what VLAN´s each access-switch is root bridge for.
    for x in `cat /tmp/170390/access-bridge-id/access-bridges.txt`; do grep -f $x /tmp/170390/root-id/* >> /tmp/170390/final/final.txt ; done