From 4c8ff652b1a7815dfc0a383b5aa7186b681cc69e Mon Sep 17 00:00:00 2001 From: Justin-p Date: Mon, 6 Apr 2020 20:21:05 +0200 Subject: [PATCH] first commit --- .editorconfig | 17 ++++++++++ .gitignore | 2 ++ .travis.yml | 29 +++++++++++++++++ README.md | 70 ++++++++++++++++++++++++++++++++++++++++++ Vagrantfile | 32 +++++++++++++++++++ defaults/main.yml | 22 +++++++++++++ handlers/main.yml | 4 +++ meta/main.yml | 16 ++++++++++ tasks/main.yml | 49 +++++++++++++++++++++++++++++ tests/inventory.yml | 37 ++++++++++++++++++++++ tests/test.yml | 4 +++ tests/travis-inventory | 1 + vars/main.yml | 12 ++++++++ 13 files changed, 295 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 Vagrantfile create mode 100644 defaults/main.yml create mode 100644 handlers/main.yml create mode 100644 meta/main.yml create mode 100644 tasks/main.yml create mode 100644 tests/inventory.yml create mode 100644 tests/test.yml create mode 100644 tests/travis-inventory create mode 100644 vars/main.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..179643b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false + +[*.md] +max_line_length = off + +[*.yml] +indent_size = 2 + +[Vagrantfile] +indent_size = 2 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95eda73 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vagrant +*.log diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b4e8e77 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +--- +language: python +python: "2.7" + +# Use the new container infrastructure +sudo: false + +# Install ansible +addons: + apt: + packages: + - python-pip + +install: + # Install ansible + - pip install ansible + + # Check ansible version + - ansible --version + + # Create ansible.cfg with correct roles_path + - printf '[defaults]\nroles_path=../' >ansible.cfg + +script: + # Basic role syntax check + - ansible-playbook tests/test.yml -i tests/travis-inventory --syntax-check + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d73d5a --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# ansible-pdc + +This role will create a brand new Primary Domain Controller with a Active Directory Domain/Forest. No hardening is applied. + +Works on + +- Windows Server 2019 +- Windows Server 2016 +- Windows Server 2012R2 + +## Requirements + +- `python3-winrm` (`pywinrm`) is needed for WinRM. + +## Role Variables + +All variables listed in `default/main.yml` reference variables from `vars/main.yml`. +If you want to change any variables overwrite the ones listed in `vars/main.yml`. + +### `vars/main.yml` + +| Variable | Default value | +|:---------------------------------|:------------------------------------| +| pdc_admin_username | administrator | +| pdc_admin_password | P@ssw0rd! | +| pdc_admin_password_never_expires | yes | +| pdc_admin_groups | ["Administrators","Domain Admins","Domain Users","Enterprise Admins","Group Policy Creator Owners","Schema Admins"] | +| pdc_domain | ad.example.test | +| pdc_netbios | TEST | +| pdc_domain_path | dc=ad,dc=example,dc=test | +| pdc_domain_safe_mode_password | P@ssw0rd! | +| pdc_domain_functional_level | Default | +| pdc_forest_functional_level | Default | +| pdc_delayed_services | ["WinRM"] | +| pdc_required_psmodules | ["ActiveDirectoryDsc"] | +| pdc_required_features | ["AD-domain-services","DNS"] | + +## Dependencies + +N/A + +## Example Playbook + + - hosts: primarydomaincontroller + roles: + - { role: justin_p.pdc } + +## Local Development + +This role includes a Vagrantfile that will spin up a local Windows Server 2019 VM in Virtualbox. +After creating the VM it will automatically run our role. + +### Development requirements + +`pip3 install pywinrm` + +#### Usage + +- Run `vagrant up` to create a VM and run our playbook +- Run `vagrant provision` to reapply our playbook +- Run `vagrant destroy -f && vagrant up` to recreate the VM and run our playbook. +- Run `vagrant destroy` to remove the VM. + +## License + +MIT + +## Authors + +- Justin Perdok ([@justin-p](https://github.com/justin-p/)), Orange Cyberdefense diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..35bba84 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,32 @@ +# Require YAML module +require 'yaml' + +# Read YAML file with box details +inventory = YAML.load_file('tests/inventory.yml') + +Vagrant.configure("2") do |config| + config.vm.define "dc" do |dc| + inventory['all']['children']['primarydomaincontroller']['hosts'].each do |server,details| + dc.vm.box = details['vagrant_box'] + dc.vm.hostname = server + dc.vm.network :private_network, ip: details['ansible_host'] + inventory['all']['vars']['vagrant_ports'].each do |protocol,details| + dc.vm.network :forwarded_port, guest: details['guest'], host: details['host'], id: protocol + end + dc.vm.provider :virtualbox do |v| + v.name = File.basename(File.dirname(__FILE__)) + "_" + server + "_" + Time.now.to_i.to_s + v.gui = false + v.memory = 2048 + v.cpus = 2 + end + end + end + inventory['all']['children']['primarydomaincontroller']['hosts'].each do |server,details| + config.vm.provision "ansible" do |ansible| + ansible.playbook = "tests/test.yml" + ansible.limit = "all" + ansible.inventory_path = "tests/inventory.yml" + ansible.verbose = "-vvvv" + end + end +end diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..9fa50eb --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,22 @@ +--- +# defaults file for ansible-pdc +# see ansible-pdc/vars/main.yml for values + +## win_domain +create_primary_domain_controller_win_domain_dns_domain_name: "{{ pdc_domain }}" +create_primary_domain_controller_win_domain_domain_netbios_name: "{{ pdc_netbios }}" +create_primary_domain_controller_win_domain_safe_mode_password: "{{ pdc_domain_safe_mode_password }}" +create_primary_domain_controller_win_domain_domain_mode: "{{ pdc_domain_functional_level }}" +create_primary_domain_controller_win_domain_forest_mode: "{{ pdc_forest_functional_level }}" + +## win_service +create_primary_domain_controller_win_service_delayed: "{{ pdc_delayed_services }}" + +## win_psmodule +create_primary_domain_controller_win_psmodule_required: "{{ pdc_required_psmodules }}" + +## win_reboot +create_primary_domain_controller_win_reboot_required_running_services: " {{pdc_required_ad_services}} " + +## win_feature +create_primary_domain_controller_win_feature_required: "{{ pdc_required_features }}" \ No newline at end of file diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..e842cf9 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,4 @@ +- name: Reboot if the Active Directory requires it + win_reboot: + post_reboot_delay: 300 + when: pdc_install_domain.reboot_required \ No newline at end of file diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..0e04d89 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + role_name: pdc + author: Justin Perdok + description: Setup a Primary Domain Controller and Active Directory on a Windows Server. + company: Orange Cyberdefense + license: MIT + min_ansible_version: 2.9 + platforms: + - name: Windows + versions: + - 2012R2 + - 2016 + - 2019 + galaxy_tags: ["activedirectory", "ad", "domaincontroller","pdc"] +dependencies: + - { role: justin_p.posh5 } \ No newline at end of file diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..9e0c8a9 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,49 @@ +--- +# tasks file for ansible-pdc +- name: Assert mandatory variables have been set + assert: { that: "{{ item }} is defined" } + with_items: + - create_primary_domain_controller_win_domain_dns_domain_name + - create_primary_domain_controller_win_domain_domain_netbios_name + - create_primary_domain_controller_win_domain_safe_mode_password + - create_primary_domain_controller_win_domain_domain_mode + - create_primary_domain_controller_win_domain_forest_mode + - create_primary_domain_controller_win_service_delayed + - create_primary_domain_controller_win_psmodule_required + - create_primary_domain_controller_win_feature_required + +- name: Ensure services start when the system has settled + win_service: + name: "{{ item }}" + start_mode: delayed + with_items: "{{ create_primary_domain_controller_win_service_delayed }}" + +- name: Check if required DSC resources and Powershell Modules are present + win_psmodule: + name: "{{ item }}" + state: present + with_items: "{{ pdc_required_psmodules }}" + +- name: Ensure there is a Active Directory domain and forest on {{ ansible_hostname }} + win_domain: + dns_domain_name: "{{ create_primary_domain_controller_win_domain_dns_domain_name }}" + domain_netbios_name: "{{ create_primary_domain_controller_win_domain_domain_netbios_name }}" + safe_mode_password: "{{ create_primary_domain_controller_win_domain_safe_mode_password }}" + domain_mode: "{{ create_primary_domain_controller_win_domain_domain_mode }}" + forest_mode: "{{ create_primary_domain_controller_win_domain_forest_mode }}" + register: pdc_install_domain + notify: Reboot if the Active Directory requires it + +- name: Force all notified handlers to run at this point, not waiting for normal sync points + meta: flush_handlers + +- name: Ensure a Domain Controller is available in the domain + win_dsc: + resource_name: WaitForADDomain + DomainName: "{{ create_primary_domain_controller_win_domain_dns_domain_name }}" + +- name: Ensure required Windows Features are installed + win_feature: + name: '{{ item }}' + include_management_tools: yes + with_items: "{{ create_primary_domain_controller_win_feature_required }}" diff --git a/tests/inventory.yml b/tests/inventory.yml new file mode 100644 index 0000000..1421bad --- /dev/null +++ b/tests/inventory.yml @@ -0,0 +1,37 @@ +all: + children: + primarydomaincontroller: + hosts: + DC2019: + ansible_host: 192.168.56.10 + vagrant_box: jborean93/WindowsServer2019 + #DC2016: + # ansible_host: 192.168.57.10 + # vagrant_box: jborean93/WindowsServer2016 + #DC2012R2: + # ansible_host: 192.168.58.10 + # vagrant_box: jborean93/WindowsServer2012R2 + vars: + ansible_user: vagrant + ansible_password: vagrant + ansible_connection: winrm + ansible_port: 5986 + ansible_winrm_transport: basic + ansible_winrm_server_cert_validation: ignore + vagrant_ports: + rdp: + guest: 3389 + host: 29500 + ssh: + guest: 22 + host: 29600 + winrm_http: + guest: 5985 + host: 29700 + winrm_https: + guest: 5986 + host: 29800 + smb: + guest: 445 + host: 29900 + include_vars: '../vars/main.yml' \ No newline at end of file diff --git a/tests/test.yml b/tests/test.yml new file mode 100644 index 0000000..310a14b --- /dev/null +++ b/tests/test.yml @@ -0,0 +1,4 @@ +--- +- hosts: all + roles: + - role: "../ansible-pdc" \ No newline at end of file diff --git a/tests/travis-inventory b/tests/travis-inventory new file mode 100644 index 0000000..d18580b --- /dev/null +++ b/tests/travis-inventory @@ -0,0 +1 @@ +localhost \ No newline at end of file diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..57262fe --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,12 @@ +--- +# vars file for ansible-pdc +# referenced by default variables ansible-pdc/defaults/main.yml + +pdc_domain: ad.example.test +pdc_netbios: TEST +pdc_domain_safe_mode_password: P@ssw0rd! +pdc_domain_functional_level: Default +pdc_forest_functional_level: Default +pdc_delayed_services: ["WinRM"] +pdc_required_psmodules: ["ActiveDirectoryDsc"] +pdc_required_features: ["AD-domain-services","DNS"] \ No newline at end of file