Post

Active Directory user management with Ansible

Here is a solution I came up with to create Active Directory users and groups with the win_domain_user and win_domain_group module. It is basically a snippet of my Active Directory role.

Default variables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# defaults/main.yml
active_directory_ensure: present

### USERS
active_directory_domain: 'example.com'
active_directory_email_domain: 'example.com'
active_directory_users: {}
active_directory_user_base_ou: 'OU=users,DC=example,DC=com'
active_directory_update_password: 'on_create'
active_directory_group_action: 'add'
active_directory_password_never_expires_enabled: false
active_directory_user_cannot_change_password_enabled: false
active_directory_account_locked_enabled: false

### GROUPS
active_directory_groups: {}
active_directory_group_base_ou: 'OU=groups,DC=example,DC=com'
active_directory_group_scope: 'global'

Groups

This task will create the AD groups and assign a mail address.

1
2
3
4
5
6
7
8
9
10
11
# tasks/configure_groups.yml
- name: configure active_directory groups
  win_domain_group:
    state: "{{ item.value.ensure | default(active_directory_ensure) }}"
    name: "{{ item.key }}"
    description: "{{ item.value.description }}"
    path: "{{ item.value.path + ',' + active_directory_group_base_ou }}"
    scope: "{{ item.value.scope | default(active_directory_group_scope) }}"
    attributes:
      mail: "{{ item.value.mail | default(item.key | lower + '@' + active_directory_email_domain) }}"
  with_dict: "{{ active_directory_groups | default({}) }}"

Users

I’m using the with_subelements loop so I can deal with a simpler data structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# tasks/configure_users.yml
- name: configure active_directory users
  win_domain_user:
    state: "{{ item.0.ensure | default(active_directory_ensure) }}"
    name: "{{ item.0.name }}"
    firstname: "{{ item.0.first_name }}"
    surname: "{{ item.0.last_name }}"
    password: "{{ item.0.password }}"
    password_never_expires: "{{ active_directory_password_never_expires_enabled }}"
    user_cannot_change_password: "{{ active_directory_user_cannot_change_password_enabled }}"
    account_locked: "{{ item.0.locked_enabled | default(active_directory_account_locked_enabled) }}"
    path: "{{ item.0.path + ',' + active_directory_user_base_ou }}"
    update_password: "{{ item.0.update_password | default(active_directory_update_password) }}"
    email: "{{ item.0.email | default(item.0.first_name | lower + '.' + item.0.last_name | lower + '@' + active_directory_email_domain) }}"
    upn: "{{ item.0.name + '@' + active_directory_domain }}"
    groups_action: "{{ item.0.groups_action | default(active_directory_group_action) }}"
    groups:
      - "{{ item.1 }}"
  with_subelements:
    - "{{ active_directory_users | default({}) }}"
    - groups

Data

Groups are represented as hashes and I also have some default groups which the corresponding user should be part of.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# groups.yml
active_directory_groups:
  # departments
  hr:
    description: 'human resource'
    path: 'OU=departments'
  ops:
    description: 'operations'
    path: 'OU=departments'
  # services
  jira-users:
    description: 'jira users'
    path: 'OU=services'
  gitlab-users:
    description: 'gitlab users'
    path: 'OU=services'

active_directory_default_groups:
  hr:
    - hr
    - jira-users
  ops:
    - ops
    - jira-users
    - gitlab-users

For users I define an array of hashes. The default groups will for instance add all Human Resource users into the hr and jira-users group.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# users.yml
active_directory_users:
  ### HR
  - name: ahr
    first_name: 'a'
    last_name: 'hr'
    password: 'test1234'
    path: 'OU=hr'
    groups: ""

  - name: bhr
    first_name: 'b'
    last_name: 'hr'
    password: 'test1234'
    path: 'OU=hr'
    groups: ""

  ### OPS
  - name: aops
    first_name: 'a'
    last_name: 'ops'
    password: 'test1234'
    path: 'OU=ops'
    groups: ""

Update May 2018: This can be managed more easily and faster since Ansible >2.5 with the flatten function and it is also possibly now to specify ldap attributes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# tasks/configure_users.yml
- name: configure active_directory employees
  win_domain_user:
    state: "{{ item.ensure | default(active_directory_ensure) }}"
    name: "{{ item.name }}"
    firstname: "{{ item.first_name }}"
    surname: "{{ item.last_name }}"
    password: "{{ item.password | default(active_directory_user_default_password) }}"
    password_never_expires: "{{ active_directory_password_never_expires_enabled }}"
    user_cannot_change_password: "{{ active_directory_user_cannot_change_password_enabled }}"
    account_locked: "{{ item.locked_enabled | default(active_directory_account_locked_enabled) }}"
    path: "{{ item.0.path + ',' + active_directory_user_base_ou }}"
    update_password: "{{ item.update_password | default(active_directory_update_password) }}"
    email: "{{ item.email | default(item.first_name | lower + '.' + item.last_name | lower + '@' + active_directory_email_domain) }}"
    upn: "{{ item.name + '@' + active_directory_domain }}"
    groups_action: "{{ item.groups_action | default(active_directory_group_action) }}"
    attributes:
      displayName: "{{ item.first_name | capitalize }} {{ item.last_name | capitalize }}"
      description: "{{ item.description | default(omit) }}"
      loginShell: "{{ item.login_shell | default(active_directory_login_shell) }}"
    groups: "{{ item.groups | default(active_directory_default_group) | flatten }}"
  with_items:
    - "{{ active_directory_users | default({}) }}"

A group every user should be member of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# groups.yml
# all users should be member of these groups
custom_group_all:
  - 'jira-users'
  - 'gitlab-users'

active_directory_default_groups:
  hr:
    - 'hr'
    - 'reporting'
    - ""
  ops:
    - 'ops'
    - 'test'
    - ""

You can also overwrite the groups per user:

1
2
3
4
5
6
7
8
9
10
11
# users.yml
active_directory_users:
  ### HR
  - name: ahr
    first_name: 'a'
    last_name: 'hr'
    password: 'test1234'
    path: 'OU=hr'
    groups:
      - ""
      - 'another_group'

Tested with:

  • Ansible 2.5.2, 2.4.2
  • Windows Server 2012R2
This post is licensed under CC BY 4.0 by the author.