- For Ubuntu 20.04 LTS
- Ansible-join-ads-ubuntu.yml
- Example:
ansible-playbook -i inventory Ansible-join-ads-ubuntu.yml -e 'ad_admin_user=Administrator' -e 'ad_admin_user_password=password' -K
- For CentOS 7:
- Ansible-join-ads-centos.yml
- Example:
ansible-playbook -i inventory Ansible-join-ads-centos.yml -e 'ad_admin_user=Administrator' -e 'ad_admin_user_password=password' -K
Last active
June 15, 2021 18:46
-
-
Save advanceboy/a7f4c4be2d3941dea2c983b4483145c7 to your computer and use it in GitHub Desktop.
Active Directory Joining Playbook for Ubuntu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
# Active Directory に参加して、 AD ユーザーでログイン可能な状態にする | |
# target: CentOS 7 | |
# example: | |
# ansible-playbook -i inventory ansible-join-ads-centos.yml -e 'ad_admin_user=Administrator' -e 'ad_admin_user_password=password' -e '[email protected]' -K | |
# refs: | |
# - https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-file_and_print_servers#setting_up_samba_as_a_domain_member | |
# - https://docs.vmware.com/en/VMware-Horizon/2103/linux-desktops-setup/GUID-F8F0CFCF-C4D6-4784-85FF-E7C6DF575F49.html | |
- hosts: all | |
vars: | |
ntp_server: ntp.nict.jp | |
realm_name: MYLOCALAD.EXAMPLE.COM | |
domain_server: mylocalad.example.com | |
ad_admin_user: '{{ ad_admin_user | default(omit) }}' # Active Directory 管理者アカウント名 | |
ad_admin_user_password: '{{ ad_admin_user_password | default(omit) }}' # Active Directory 管理者パスワード | |
no_reboot: no | |
permitted_users: null | |
tasks: | |
- name: fails if initial hostname | |
fail: | |
msg: 'failed as invalid hostname: ({{ansible_fqdn}})' | |
when: '"localhost" in ansible_fqdn' | |
# install pexpect pip package for ansible expect module | |
- name: update pre-setting packages | |
become: yes | |
yum: | |
name: epel-release | |
state: present | |
- name: install pip | |
become: yes | |
yum: | |
name: python-pip | |
state: present | |
- name: install pexpect | |
become: yes | |
pip: | |
name: pexpect | |
state: present | |
- name: install ad modules | |
become: yes | |
register: result_yum_samba | |
yum: | |
name: | |
- samba-winbind-clients | |
- samba-winbind-krb5-locator | |
- krb5-workstation | |
- chrony | |
state: present | |
- name: check whether samba has been updated or not | |
set_fact: | |
samba_daemon_state: '{{ "restarted" if result_yum_samba is changed else "started" }}' | |
# 時刻同期サーバを追加 | |
- name: add AD to chrony sources | |
become: yes | |
register: result_chrony_conf | |
blockinfile: | |
path: /etc/chrony.conf | |
insertbefore: BOF | |
block: server {{ ntp_server }} iburst | |
- name: ensure chrony service | |
become: yes | |
service: | |
name: chronyd | |
state: '{{ "restarted" if result_chrony_conf is changed else "started" }}' | |
enabled: yes | |
# samba net で AD 参加されているかの確認 | |
- name: check if machine is bound to AD | |
become: yes | |
register: result_netads_bound | |
# AD 未参加の状態では パスワードを聞かれるので、空行をパイプして入力をスキップする | |
shell: "echo '' | net ads testjoin" | |
changed_when: false | |
failed_when: false | |
- name: | |
set_fact: | |
needs_join_ad: '{{ result_netads_bound.rc != 0 or "Join is OK" not in result_netads_bound.stdout }}' | |
# AD 管理者 のユーザー名とパスワードの設定の確認 | |
- name: ad_admin_user and ad_admin_user_password should be defined | |
assert: | |
that: | |
- ad_admin_user is defined and ad_admin_user != '' | |
- ad_admin_user_password is defined and ad_admin_user_password != '' | |
fail_msg: Set the value of 'ad_admin_user' and 'ad_admin_user_password' in the extra variables (or Job Template Survey in AWX). | |
when: needs_join_ad | |
# AD参加に必要な部分のみの smb.conf, nsswitch.conf, krb5.conf の更新 | |
# ログインユーザーはシングルドメイン前提としているため id のマッピングは rid としている。 | |
- name: update smb.conf for join to AD | |
become: yes | |
ini_file: | |
path: /etc/samba/smb.conf | |
create: no | |
section: global | |
option: '{{ item.opt }}' | |
value: '{{ item.val }}' | |
with_items: | |
- { opt: workgroup, val: '{{ realm_name.split(".")[0] }}' } | |
- { opt: security, val: ads } | |
- { opt: kerberos method, val: system keytab } | |
- { opt: template homedir, val: /home/%U } | |
- { opt: template shell, val: /bin/bash } | |
- { opt: realm, val: '{{ realm_name }}' } | |
- { opt: 'idmap config * : range', val: 10000-999999 } | |
- { opt: 'idmap config * : backend', val: tdb } | |
- { opt: 'idmap config {{ realm_name.split(".")[0] }} : range', val: 2000000-2999999 } | |
- { opt: 'idmap config {{ realm_name.split(".")[0] }} : backend', val: rid } | |
- { opt: winbind use default domain, val: 'yes' } | |
- { opt: winbind refresh tickets, val: 'yes' } | |
- { opt: winbind offline logon, val: 'yes' } | |
- { opt: winbind enum users, val: 'yes' } | |
- { opt: winbind enum groups, val: 'yes' } | |
- name: update nsswitch.conf | |
become: yes | |
lineinfile: | |
path: /etc/nsswitch.conf | |
backrefs: yes | |
regexp: '^({{ item }}:\s*(?!.*winbind).*)$' | |
line: '\1 winbind' | |
with_items: | |
- passwd | |
- group | |
- name: update krb5.conf | |
become: yes | |
copy: | |
dest: /etc/krb5.conf | |
owner: root | |
group: root | |
mode: u=rw,g=r,o=r | |
content: | | |
[logging] | |
default = FILE:/var/log/krb5libs.log | |
kdc = FILE:/var/log/krb5kdc.log | |
admin_server = FILE:/var/log/kadmind.log | |
[libdefaults] | |
default_realm = {{ realm_name }} | |
forwardable = true | |
proxiable = true | |
dns_lookup_realm = false | |
renew_lifetime = 7d | |
rdns = false | |
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt | |
default_ccache_name = KEYRING:persistent:%{uid} | |
[realms] | |
{{ realm_name }} = { | |
kdc = {{ domain_server }} | |
admin_server = {{ domain_server }} | |
} | |
[domain_realm] | |
.{{ domain_server }} = {{ realm_name }} | |
{{ domain_server }} = {{ realm_name }} | |
# net ads を使って AD に参加 | |
- when: needs_join_ad | |
block: | |
- name: get kerberos ticket | |
become: yes | |
expect: | |
command: '/usr/bin/kinit {{ ad_admin_user }}' | |
responses: | |
'(?i)Password for ': '{{ ad_admin_user_password }}' | |
timeout: 300 | |
no_log: yes | |
- name: join to Active Directory | |
become: yes | |
expect: | |
command: 'net ads join --no-dns-updates -U {{ ad_admin_user }}' | |
responses: | |
"(?i)Enter .*'s password:": '{{ ad_admin_user_password }}' | |
no_log: yes | |
# winbind は kinit 実行後に起動する必要がある。 | |
- name: ensure winbind is {{ samba_daemon_state }} | |
become: yes | |
systemd: | |
name: "{{ item }}" | |
state: '{{ samba_daemon_state }}' | |
enabled: yes | |
with_items: | |
- winbind | |
# pam の設定で 'Winbind 認証' または 'ホームディレクトリーを作成' が無効になっていたら、有効にする | |
- name: check pam winbind-auth profile | |
shell: >- | |
cat /etc/pam.d/* | grep -E '^session\s+optional\s+pam_winbind.so' | |
changed_when: no | |
failed_when: no | |
register: result_grep_pam_winbind_auth | |
- name: check pam mkhomedir profile | |
shell: >- | |
cat /etc/pam.d/* | grep -E '^session\s+optional\s+pam_mkhomedir.so' | |
changed_when: no | |
failed_when: no | |
register: result_grep_pam_mkhomedir | |
- name: ensure pam winbind-auth and mkhomedir profile is enabled | |
become: yes | |
shell: authconfig --enablewinbind --enablewinbindauth --enablemkhomedir --update | |
when: result_grep_pam_winbind_auth.rc != 0 or result_grep_pam_mkhomedir.rc != 0 | |
# permitted_users が存在する場合は、 AD で Linux にログインできるアカウントを、 ユーザーまたはグループ単位で制限 | |
- name: update pam_winbind.conf for restricting login users | |
become: yes | |
ini_file: | |
path: /etc/security/pam_winbind.conf | |
create: yes | |
state: '{{ "present" if permitted_users is not none else "absent" }}' | |
section: global | |
option: require_membership_of | |
value: '{{ permitted_users | join(",") if (permitted_users is iterable and permitted_users is not string) else permitted_users }}' | |
register: result_pam_winbind_require_member | |
# GUI ログイン画面で, AD アカウントを使ったログインを可能にするため、システムを再起動する。 | |
# no_reboot 追加変数を設定していた場合、再起動は行わない。 | |
- when: | |
- not (no_reboot) | |
- result_yum_samba is changed or result_pam_winbind_require_member is changed | |
block: | |
- name: start rebooting | |
become: yes | |
shell: 'sleep 5 && reboot' | |
async: 1 | |
poll: 0 # タスクの完了を待たずに次へ | |
- name: wait for rebooted | |
wait_for_connection: | |
delay: 30 | |
timeout: 300 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
# Active Directory に参加して、 AD ユーザーでログイン可能な状態にする | |
# target: Ubuntu 20.04 LTS | |
# example: | |
# ansible-playbook -i inventory ansible-join-ads-ubuntu.yml -e 'ad_admin_user=Administrator' -e 'ad_admin_user_password=password' -e '[email protected]' -K | |
# refs: | |
# - https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-file_and_print_servers#setting_up_samba_as_a_domain_member | |
# - https://docs.vmware.com/en/VMware-Horizon/2103/linux-desktops-setup/GUID-F8F0CFCF-C4D6-4784-85FF-E7C6DF575F49.html | |
- hosts: all | |
vars: | |
ntp_server: ntp.nict.jp | |
realm_name: MYLOCALAD.EXAMPLE.COM | |
domain_server: mylocalad.example.com | |
ad_admin_user: '{{ ad_admin_user | default(omit) }}' # Active Directory 管理者アカウント名 | |
ad_admin_user_password: '{{ ad_admin_user_password | default(omit) }}' # Active Directory 管理者パスワード | |
no_reboot: no | |
permitted_users: null | |
tasks: | |
- name: fails if initial hostname | |
fail: | |
msg: 'failed as invalid hostname: ({{ansible_fqdn}})' | |
when: '"localhost" in ansible_fqdn' | |
# install pexpect pip package for ansible expect module | |
- name: install pip | |
become: yes | |
apt: | |
update_cache: yes | |
name: python3-pip | |
state: present | |
- name: install pexpect | |
become: yes | |
pip: | |
name: pexpect | |
state: present | |
- name: install ad modules | |
become: yes | |
register: result_apt_samba | |
apt: | |
name: | |
- libnss-winbind | |
- libpam-winbind | |
- krb5-user | |
- chrony | |
state: present | |
- name: check whether samba has been updated or not | |
set_fact: | |
samba_daemon_state: '{{ "restarted" if result_apt_samba is changed else "started" }}' | |
# 時刻同期サーバを追加 | |
- name: add AD to chrony sources | |
become: yes | |
register: result_chrony_conf | |
blockinfile: | |
path: /etc/chrony/chrony.conf | |
insertbefore: BOF | |
block: server {{ ntp_server }} iburst | |
- name: ensure chrony service | |
become: yes | |
service: | |
name: chrony.service | |
state: '{{ "restarted" if result_chrony_conf is changed else "started" }}' | |
enabled: yes | |
# samba net で AD 参加されているかの確認 | |
- name: check if machine is bound to AD | |
become: yes | |
register: result_netads_bound | |
# AD 未参加の状態では パスワードを聞かれるので、空行をパイプして入力をスキップする | |
shell: "echo '' | net ads testjoin" | |
changed_when: false | |
failed_when: false | |
- name: | |
set_fact: | |
needs_join_ad: '{{ result_netads_bound.rc != 0 or "Join is OK" not in result_netads_bound.stdout }}' | |
# AD 管理者 のユーザー名とパスワードの設定の確認 | |
- name: ad_admin_user and ad_admin_user_password should be defined | |
assert: | |
that: | |
- ad_admin_user is defined and ad_admin_user != '' | |
- ad_admin_user_password is defined and ad_admin_user_password != '' | |
fail_msg: Set the value of 'ad_admin_user' and 'ad_admin_user_password' in the extra variables (or Job Template Survey in AWX). | |
when: needs_join_ad | |
# AD参加に必要な部分のみの smb.conf, nsswitch.conf, krb5.conf の更新 | |
# ログインユーザーはシングルドメイン前提としているため id のマッピングは rid としている。 | |
- name: update smb.conf for join to AD | |
become: yes | |
ini_file: | |
path: /etc/samba/smb.conf | |
create: no | |
section: global | |
option: '{{ item.opt }}' | |
value: '{{ item.val }}' | |
with_items: | |
- { opt: workgroup, val: '{{ realm_name.split(".")[0] }}' } | |
- { opt: security, val: ads } | |
- { opt: kerberos method, val: system keytab } | |
- { opt: template homedir, val: /home/%U } | |
- { opt: template shell, val: /bin/bash } | |
- { opt: realm, val: '{{ realm_name }}' } | |
- { opt: 'idmap config * : range', val: 10000-999999 } | |
- { opt: 'idmap config * : backend', val: tdb } | |
- { opt: 'idmap config {{ realm_name.split(".")[0] }} : range', val: 2000000-2999999 } | |
- { opt: 'idmap config {{ realm_name.split(".")[0] }} : backend', val: rid } | |
- { opt: winbind use default domain, val: 'yes' } | |
- { opt: winbind refresh tickets, val: 'yes' } | |
- { opt: winbind offline logon, val: 'yes' } | |
- { opt: winbind enum users, val: 'yes' } | |
- { opt: winbind enum groups, val: 'yes' } | |
- name: update nsswitch.conf | |
become: yes | |
lineinfile: | |
path: /etc/nsswitch.conf | |
backrefs: yes | |
regexp: '^({{ item }}:\s*(?!.*winbind).*)$' | |
line: '\1 winbind' | |
with_items: | |
- passwd | |
- group | |
- name: update krb5.conf | |
become: yes | |
copy: | |
dest: /etc/krb5.conf | |
owner: root | |
group: root | |
mode: u=rw,g=r,o=r | |
content: | | |
[logging] | |
default = FILE:/var/log/krb5libs.log | |
kdc = FILE:/var/log/krb5kdc.log | |
admin_server = FILE:/var/log/kadmind.log | |
[libdefaults] | |
default_realm = {{ realm_name }} | |
forwardable = true | |
proxiable = true | |
dns_lookup_realm = false | |
renew_lifetime = 7d | |
rdns = false | |
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt | |
default_ccache_name = KEYRING:persistent:%{uid} | |
[realms] | |
{{ realm_name }} = { | |
kdc = {{ domain_server }} | |
admin_server = {{ domain_server }} | |
} | |
[domain_realm] | |
.{{ domain_server }} = {{ realm_name }} | |
{{ domain_server }} = {{ realm_name }} | |
# pam の設定で 'Create home directory on login' が無効になっていたら、有効にする | |
- name: check pam mkhomedir profile | |
shell: >- | |
cat /etc/pam.d/common-* | grep -E '^session\s+optional\s+pam_mkhomedir.so' | |
changed_when: no | |
failed_when: no | |
register: result_grep_pam_mkhomedir | |
- name: ensure pam mkhomedir profile is enabled | |
become: yes | |
shell: pam-auth-update --enable mkhomedir | |
when: result_grep_pam_mkhomedir.rc != 0 | |
# net ads を使って AD に参加 | |
- when: needs_join_ad | |
block: | |
- name: get kerberos ticket | |
become: yes | |
expect: | |
command: '/usr/bin/kinit {{ ad_admin_user }}' | |
responses: | |
'(?i)Password for ': '{{ ad_admin_user_password }}' | |
timeout: 300 | |
no_log: yes | |
- name: join to Active Directory | |
become: yes | |
expect: | |
command: 'net ads join --no-dns-updates -U {{ ad_admin_user }}' | |
responses: | |
"(?i)Enter .*'s password:": '{{ ad_admin_user_password }}' | |
no_log: yes | |
# winbind は kinit 実行後に起動する必要がある。 | |
- name: ensure winbind is {{ samba_daemon_state }} | |
become: yes | |
systemd: | |
name: "{{ item }}" | |
state: '{{ samba_daemon_state }}' | |
enabled: yes | |
with_items: | |
- winbind | |
# permitted_users が存在する場合は、 AD で Linux にログインできるアカウントを、 ユーザーまたはグループ単位で制限 | |
- name: update pam_winbind.conf for restricting login users | |
become: yes | |
ini_file: | |
path: /etc/security/pam_winbind.conf | |
create: yes | |
state: '{{ "present" if permitted_users is not none else "absent" }}' | |
section: global | |
option: require_membership_of | |
value: '{{ permitted_users | join(",") if (permitted_users is iterable and permitted_users is not string) else permitted_users }}' | |
register: result_pam_winbind_require_member | |
# GUI ログイン画面で, AD アカウントを使ったログインを可能にするため、システムを再起動する。 | |
# no_reboot 追加変数を設定していた場合、再起動は行わない。 | |
- when: | |
- not (no_reboot) | |
- result_apt_samba is changed or result_pam_winbind_require_member is changed | |
block: | |
- name: start rebooting | |
become: yes | |
shell: 'sleep 5 && reboot' | |
async: 1 | |
poll: 0 # タスクの完了を待たずに次へ | |
- name: wait for rebooted | |
wait_for_connection: | |
delay: 30 | |
timeout: 300 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MIT License | |
Copyright (c) 2021 advanceboy | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment