IIS

How to deploy multiple sites in IIS with Ansible

This was fun and interesting little project. Looks like nobody ever really got it working properly, so I decided to do it.

This role will install IIS if its not already installed
It will also configure all the user permissions needed for IIS
It will configure a default application pool
This role will allow you to call the ansible role once and create multiple sites in IIS and attach the sites to the default application pool
It will also configure the bindings
You can also remove, start, and stop sites

Additions that are not in any public repo online currently. If you require these additions for your organisation, you may hire or pay for the additions:
• Ability to create multiple application pools per site.
• Set application pool identities
• Update IIS site paths to use a NAS location vs physical paths only

Note: You will likely have to add the firewall rules depending on how your network setup. I have written a windows playbook to handle the firewall stuff, but that will be in another post. You will also need to ensure winrm is configured properly on the windows machine for ansible to talk to it.

Ansible Operational Documentation:

2.Now you want to edit the hosts.client file name file or create it if it doesn’t exist under your “ansible/inventory/dev:staging:prod” directory. This is a good way to separate environments with ansible, inside each environment you should have a hosts.file like indicated below.

Example file: hosts.dev, hosts.staging, hosts.prod

b.Put your server under the appropriate group inside the file and save
i.devops.nicktailor.win ansible_host=192.168.90.10

Note: If there is no group simply list the server outside grouping, the –limit flag will pick it

up.

3.Now inside this directory you should see hosts & host_vars, group_vars

Descriptions:

c.Hosts. – is where you will list your servers under specific groups which tell the playbook (what the server is, if it the server should have a specific task run on it, and how to find it)
d.Host_vars – Inside this directory is where you list the server by name which is you will list under hosts. Inside these files you pass variable parameters to the specific roles when running your playbook. Without these the playbook cant do the tasks you want it to.
e.Group_varsAre how a way to group variables for sets of servers and this keeps code cleaners and easier to manage.

Operational Use:

4.Move inside host_var
f.cd host_var
g.create a file called {{ servername }} and save it for us its devops.nicktailor.win

.

5.Now inside this directory you should see hosts & host_vars, group_vars

Descriptions:

h.Hosts. – is where you will list your servers under specific groups which tell the playbook (what the server is, if it the server should have a specific task run on it, and how to find it)
i.Host_vars – Inside this directory is where you list the server by name which is you will list under hosts. Inside these files you pass variable parameters to the specific roles when running your playbook. Without these the playbook cant do the tasks you want it to.
j.Group_varsAre how a way to group variables for sets of servers and this keeps code cleaners and easier to manage.

Operational Use:

6.Move inside host_var
k.cd host_var
l.create a file called {{ servername }} and save it for us its devops.nicktailor.win

Okay now here is where VSC is handy. You want to connect your visual studio code to the management server under your user.

.

Note: You don’t have to use VSC you can use good old nano or vim, but it’s a pain. Up to you.

.

Example files:

ansible/inventory/dev/host_vars/devops.nicktailor.win

Example Yaml Block :

.

domains:

– name: “First website”

ip: ‘*’

iis_binding_port: ‘8082’

protocol: ‘http’

state: ‘absent’

certificate_hash:

certificate_store_name: ‘My’

iis_site_name: ‘Default Web Site’

iis_site_path: ‘C:\inetpub\wwwroot1′

iis_acl_path:  ‘C:\inetpub\wwwroot1′

iis_site_state: absent

iis_site_port: ’80’

iis_site_id:

iis_site_ip: ‘*’

iis_site_ssl: false

iis_site_hostname: ‘*’

iis_site_parameters:

iis_site_state_start: stopped

iis_site_web_config:

iis_site_web_config_force: true

.

– name: “Second website”

ip: ‘*’

iis_binding_port: ‘8081’

protocol: ‘http’

state: ‘present’

certificate_hash:

certificate_store_name: ‘My’

iis_site_name: ‘site2’

iis_acl_path: ‘C:\inetpub\wwwroot2′

iis_site_path: ‘C:\inetpub\wwwroot2′

iis_site_state: present

iis_site_port: ’80’

iis_site_id:

iis_site_ip: ‘*’

iis_site_ssl: false

iis_site_hostname: ‘*’

iis_site_parameters:

iis_site_state_start: started

iis_site_web_config:

iis_site_web_config_force: true

.

1.You must run your play book from inside parent directory always “ansible
2.Now there is a playbook called nickwiniis.yml in the ansible directory which simply calls the ansible-role-win-iis role inside the roles directory.

Example: of ansible/nickwiniis.yml

hosts: all

gather_facts: yes

any_errors_fatal: true

roles:

– role: ansible-role-win-iis

.

Command:

ansible-playbook –i inventory/dev/hosts nickwiniis.yml -u nick –Kkb –limit=’devops.nicktailor.win

-i : This flag tells ansibe-playbook command which hosts file to use, these are always defined by environment like hosts.dev or hosts.staging
-u : this is the ssh_user you will be connecting to the servers with
-Kkb : this tells ansible that you will be using sudo su – for the ssh_user when running all role/tasks
-ask-beocme : is saying become root
-limit=’server’ : this allows you to segement which server you want to run the playbook against.

.

Successful example run of the book:

.

[ntailor@ansible.nicktailor.com]$ansibleplaybooki hosts/dev nickwiniis.ymllimit=devops.nicktailor.win . PLAY [all] ***************************************************************************************************************************************************** . TASK [Gathering Facts] ***************************************************************************************************************************************** ok: [devops.nicktailor.win] . TASK [ansiblerolewiniis : ensure iis is installed] ********************************************************************************************************** ok: [devops.nicktailor.win] . TASK [ansiblerolewiniis : configure app pool] *************************************************************************************************************** ok: [devops.nicktailor.win] . TASK [ansiblerolewiniis : ensure path for site exists] ****************************************************************************************************** ok: [devops.nicktailor.win] => (item={‘name’: ‘First website’, host_header: , ip: ‘*’, iis_binding_port: ‘8082’, ‘protocol’: ‘http’, ‘state’: ‘absent’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘Default Web Site’, iis_site_path: ‘C:\\inetpub\\wwwroot1′, iis_acl_path: ‘C:\\inetpub\\wwwroot1′, iis_site_state: ‘absent’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘stopped’, iis_site_web_config: , iis_site_web_config_force: True}) ok: [devops.nicktailor.win] => (item={‘name’: ‘Second website’, host_header: , ip: ‘*’, iis_binding_port: ‘8081’, ‘protocol’: ‘http’, ‘state’: ‘present’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘site2’, iis_acl_path: ‘C:\\inetpub\\wwwroot2′, iis_site_path: ‘C:\\inetpub\\wwwroot2′, iis_site_state: ‘present’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘started’, iis_site_web_config: , iis_site_web_config_force: True}) . TASK [ansiblerolewiniis : debug] **************************************************************************************************************************** ok: [devops.nicktailor.win] => { “path”: { “changed”: false, msg: “All items completed”, “results”: [ { ansible_loop_var: “item”, “changed”: false, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot1″, iis_binding_port: “8082”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “Default Web Site”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot1″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “absent”, iis_site_state_start: “stopped”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “First website”, “protocol”: “http”, “state”: “absent” } }, { ansible_loop_var: “item”, “changed”: false, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot2″, iis_binding_port: “8081”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “site2”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot2″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “present”, iis_site_state_start: “started”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “Second website”, “protocol”: “http”, “state”: “present” } } ] } } . TASK [ansiblerolewiniis : allow iis group access to site path] ********************************************************************************************** ok: [devops.nicktailor.win] => (item={‘name’: ‘First website’, host_header: , ip: ‘*’, iis_binding_port: ‘8082’, ‘protocol’: ‘http’, ‘state’: ‘absent’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘Default Web Site’, iis_site_path: ‘C:\\inetpub\\wwwroot1′, iis_acl_path: ‘C:\\inetpub\\wwwroot1′, iis_site_state: ‘absent’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘stopped’, iis_site_web_config: , iis_site_web_config_force: True}) ok: [devops.nicktailor.win] => (item={‘name’: ‘Second website’, host_header: , ip: ‘*’, iis_binding_port: ‘8081’, ‘protocol’: ‘http’, ‘state’: ‘present’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘site2’, iis_acl_path: ‘C:\\inetpub\\wwwroot2′, iis_site_path: ‘C:\\inetpub\\wwwroot2′, iis_site_state: ‘present’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘started’, iis_site_web_config: , iis_site_web_config_force: True}) . TASK [ansiblerolewiniis : debug] **************************************************************************************************************************** ok: [devops.nicktailor.win] => { “access”: { “changed”: false, msg: “All items completed”, “results”: [ { ansible_loop_var: “item”, “changed”: false, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot1″, iis_binding_port: “8082”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “Default Web Site”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot1″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “absent”, iis_site_state_start: “stopped”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “First website”, “protocol”: “http”, “state”: “absent” } }, { ansible_loop_var: “item”, “changed”: false, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot2″, iis_binding_port: “8081”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “site2”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot2″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “present”, iis_site_state_start: “started”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “Second website”, “protocol”: “http”, “state”: “present” } } ] } } . TASK [ansiblerolewiniis : upload custom web.config from template] ******************************************************************************************* skipping: [devops.nicktailor.win] => (item={‘name’: ‘First website’, host_header: , ip: ‘*’, iis_binding_port: ‘8082’, ‘protocol’: ‘http’, ‘state’: ‘absent’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘Default Web Site’, iis_site_path: ‘C:\\inetpub\\wwwroot1′, iis_acl_path: ‘C:\\inetpub\\wwwroot1′, iis_site_state: ‘absent’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘stopped’, iis_site_web_config: , iis_site_web_config_force: True}) skipping: [devops.nicktailor.win] => (item={‘name’: ‘Second website’, host_header: , ip: ‘*’, iis_binding_port: ‘8081’, ‘protocol’: ‘http’, ‘state’: ‘present’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘site2’, iis_acl_path: ‘C:\\inetpub\\wwwroot2′, iis_site_path: ‘C:\\inetpub\\wwwroot2′, iis_site_state: ‘present’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘started’, iis_site_web_config: , iis_site_web_config_force: True}) . TASK [ansiblerolewiniis : debug] **************************************************************************************************************************** ok: [devops.nicktailor.win] => { startiis: { “changed”: false, msg: “All items completed”, “results”: [ { ansible_loop_var: “item”, “changed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot1″, iis_binding_port: “8082”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “Default Web Site”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot1″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “absent”, iis_site_state_start: “stopped”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “First website”, “protocol”: “http”, “state”: “absent” }, skip_reason: “Conditional result was False”, “skipped”: true }, { ansible_loop_var: “item”, “changed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot2″, iis_binding_port: “8081”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “site2”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot2″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “present”, iis_site_state_start: “started”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “Second website”, “protocol”: “http”, “state”: “present” }, skip_reason: “Conditional result was False”, “skipped”: true } ] } } . TASK [ansiblerolewiniis : configure site] ******************************************************************************************************************* changed: [devops.nicktailor.win] => (item={‘name’: ‘First website’, host_header: , ip: ‘*’, iis_binding_port: ‘8082’, ‘protocol’: ‘http’, ‘state’: ‘absent’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘Default Web Site’, iis_site_path: ‘C:\\inetpub\\wwwroot1′, iis_acl_path: ‘C:\\inetpub\\wwwroot1′, iis_site_state: ‘absent’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘stopped’, iis_site_web_config: , iis_site_web_config_force: True}) changed: [devops.nicktailor.win] => (item={‘name’: ‘Second website’, host_header: , ip: ‘*’, iis_binding_port: ‘8081’, ‘protocol’: ‘http’, ‘state’: ‘present’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘site2’, iis_acl_path: ‘C:\\inetpub\\wwwroot2′, iis_site_path: ‘C:\\inetpub\\wwwroot2′, iis_site_state: ‘present’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘started’, iis_site_web_config: , iis_site_web_config_force: True}) . TASK [ansiblerolewiniis : debug] **************************************************************************************************************************** ok: [devops.nicktailor.win] => { iis_site: { “changed”: true, msg: “All items completed”, “results”: [ { ansible_loop_var: “item”, “changed”: true, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot1″, iis_binding_port: “8082”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “Default Web Site”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot1″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “absent”, iis_site_state_start: “stopped”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “First website”, “protocol”: “http”, “state”: “absent” }, “site”: { ApplicationPool: DefaultAppPool, “Bindings”: [ “*:80:* ], “ID”: 1, “Name”: “Default Web Site”, PhysicalPath: “C:\\inetpub\\wwwroot1″, “State”: “Stopped” } }, { ansible_loop_var: “item”, “changed”: true, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot2″, iis_binding_port: “8081”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “site2”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot2″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “present”, iis_site_state_start: “started”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “Second website”, “protocol”: “http”, “state”: “present” }, “site”: { ApplicationPool: DefaultAppPool, “Bindings”: [ “*:80:* ], “ID”: 2, “Name”: “site2”, PhysicalPath: “C:\\inetpub\\wwwroot2″, “State”: “Started” } } ] } } . TASK [ansiblerolewiniis : configure site bindings] ********************************************************************************************************** ok: [devops.nicktailor.win] => (item={‘name’: ‘First website’, host_header: , ip: ‘*’, iis_binding_port: ‘8082’, ‘protocol’: ‘http’, ‘state’: ‘absent’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘Default Web Site’, iis_site_path: ‘C:\\inetpub\\wwwroot1′, iis_acl_path: ‘C:\\inetpub\\wwwroot1′, iis_site_state: ‘absent’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘stopped’, iis_site_web_config: , iis_site_web_config_force: True}) changed: [devops.nicktailor.win] => (item={‘name’: ‘Second website’, host_header: , ip: ‘*’, iis_binding_port: ‘8081’, ‘protocol’: ‘http’, ‘state’: ‘present’, certificate_hash: , certificate_store_name: ‘My’, iis_site_name: ‘site2’, iis_acl_path: ‘C:\\inetpub\\wwwroot2′, iis_site_path: ‘C:\\inetpub\\wwwroot2′, iis_site_state: ‘present’, iis_site_port: ’80’, iis_site_id: , iis_site_ip: ‘*’, iis_site_ssl: False, iis_site_hostname: ‘*’, iis_site_parameters: , iis_site_state_start: ‘started’, iis_site_web_config: , iis_site_web_config_force: True}) . TASK [ansiblerolewiniis : debug] **************************************************************************************************************************** ok: [devops.nicktailor.win] => { “startiis2”: { “changed”: true, msg: “All items completed”, “results”: [ { ansible_loop_var: “item”, “changed”: false, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot1″, iis_binding_port: “8082”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “Default Web Site”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot1″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “absent”, iis_site_state_start: “stopped”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “First website”, “protocol”: “http”, “state”: “absent” } }, { ansible_loop_var: “item”, binding_info: { bindingInformation: “*:8081:”, certificateHash: “”, certificateStoreName: “”, hostheader: “”, ip: “*”, “port”: 8081, “protocol”: “http”, sslFlags: 0 }, “changed”: true, “failed”: false, “item”: { certificate_hash: “”, certificate_store_name: “My”, host_header: “”, iis_acl_path: “C:\\inetpub\\wwwroot2″, iis_binding_port: “8081”, iis_site_hostname: “*”, iis_site_id: “”, iis_site_ip: “*”, iis_site_name: “site2”, iis_site_parameters: “”, iis_site_path: “C:\\inetpub\\wwwroot2″, iis_site_port: “80”, iis_site_ssl: false, iis_site_state: “present”, iis_site_state_start: “started”, iis_site_web_config: “”, iis_site_web_config_force: true, ip: “*”, “name”: “Second website”, “protocol”: “http”, “state”: “present” }, operation_type: “added”, website_state: “Started” } ] } } . RUNNING HANDLER [ansiblerolewiniis : restart iis] *********************************************************************************************************** changed: [devops.nicktailor.win] . PLAY RECAP ***************************************************************************************************************************************************** devops.nicktailor.win : ok=13 changed=3 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 . . Now you could write in to copy an test index.html file, but just to show it works. I manually created the index.html . Test: [ansible.nicktailor.com]$ curl -k http://devops.nicktailor.win

YAY THis works!

.

.