diff --git a/library/web_infrastructure/htpasswd b/library/web_infrastructure/htpasswd
new file mode 100644
index 0000000000..e594280700
--- /dev/null
+++ b/library/web_infrastructure/htpasswd
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# (c) 2013, Nimbis Services, Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+DOCUMENTATION = """
+module: htpasswd
+short_description: manage user files for basic authentication
+description:
+ - Add and remove username/password entries in a password file using htpasswd.
+ - This is used by web servers such as Apache and Nginx for basic authentication.
+options:
+ path:
+ required: true
+ aliases: [ dest, destfile ]
+ description:
+ - Path to the file that contains the usernames and passwords
+ name:
+ required: true
+ aliases: [ username ]
+ description:
+ - User name to add or remove
+ password:
+ required: false
+ description:
+ - Password associated with user.
+ - Must be specified if user does not exist yet
+ state:
+ required: false
+ choices: [ present, absent ]
+ default: "present"
+ description:
+ - Whether the user entry should be present or not
+ create:
+ required: false
+ choices: [ "yes", "no" ]
+ default: "yes"
+ description:
+ - Used with C(state=present). If specified, the file will be created
+ if it does not already exist. If set to "no", will fail if the
+ file does not exist
+requires: [ passlib>=1.6 ]
+author: Lorin Hochstein
+"""
+
+EXAMPLES = """
+# Add a user to a password file and ensure permissions are set
+- htpasswd: path=/etc/nginx/passwdfile name=janedoe password=9s36?;fyNp owner=root group=www-data mode=0640
+# Remove a user from a password file
+- htpasswd: path=/etc/apache2/passwdfile name=foobar state=absent
+"""
+
+
+import os
+
+try:
+ from passlib.apache import HtpasswdFile
+except ImportError:
+ passlib_installed = False
+else:
+ passlib_installed = True
+
+
+def create_missing_directories(dest):
+ destpath = os.path.dirname(dest)
+ if not os.path.exists(destpath):
+ os.makedirs(destpath)
+
+
+def present(dest, username, password, create, check_mode):
+ """ Ensures user is present
+
+ Returns (msg, changed) """
+ if not os.path.exists(dest):
+ if not create:
+ raise ValueError('Destination %s does not exist' % dest)
+ if check_mode:
+ return ("Create %s" % dest, True)
+ create_missing_directories(dest)
+ ht = HtpasswdFile(dest, new=True)
+ ht.set_password(username, password)
+ ht.save()
+ return ("Created %s and added %s" % (dest, username), True)
+ else:
+ ht = HtpasswdFile(dest, new=False)
+ if ht.check_password(username, password):
+ return ("%s already present" % username, False)
+ else:
+ if not check_mode:
+ ht.set_password(username, password)
+ ht.save()
+ return ("Add/update %s" % username, True)
+
+
+def absent(dest, username, check_mode):
+ """ Ensures user is absent
+
+ Returns (msg, changed) """
+ if not os.path.exists(dest):
+ raise ValueError("%s does not exists" % dest)
+
+ ht = HtpasswdFile(dest, new=False)
+ if username not in ht.users():
+ return ("%s not present" % username, False)
+ else:
+ if not check_mode:
+ ht.delete(username)
+ ht.save()
+ return ("Remove %s" % username, True)
+
+
+def check_file_attrs(module, changed, message):
+
+ file_args = module.load_file_common_arguments(module.params)
+ if module.set_file_attributes_if_different(file_args, False):
+
+ if changed:
+ message += " and "
+ changed = True
+ message += "ownership, perms or SE linux context changed"
+
+ return message, changed
+
+
+def main():
+ arg_spec = dict(
+ path=dict(required=True, aliases=["dest", "destfile"]),
+ name=dict(required=True, aliases=["username"]),
+ password=dict(required=False, default=None),
+ state=dict(required=False, default="present"),
+ create=dict(type='bool', choices=BOOLEANS, default='yes'),
+
+ )
+ module = AnsibleModule(argument_spec=arg_spec,
+ add_file_common_args=True,
+ supports_check_mode=True)
+
+ path = module.params['path']
+ username = module.params['name']
+ password = module.params['password']
+ state = module.params['state']
+ create = module.params['create']
+ check_mode = module.check_mode
+
+ if not passlib_installed:
+ module.fail_json(msg="This module requires the passlib Python library")
+
+ try:
+ if state == 'present':
+ (msg, changed) = present(path, username, password, create, check_mode)
+ elif state == 'absent':
+ (msg, changed) = absent(path, username, check_mode)
+ else:
+ module.fail_json(msg="Invalid state: %s" % state)
+
+ check_file_attrs(module, changed, msg)
+ module.exit_json(msg=msg, changed=changed)
+ except Exception, e:
+ module.fail_json(msg=str(e))
+
+
+# this is magic, see lib/ansible/module_common.py
+#<>
+
+if __name__ == '__main__':
+ main()