diff --git a/src/account/actions.py b/src/account/actions.py
new file mode 100644
index 0000000000000000000000000000000000000000..119db34d61e608fb8c6a8a9c4e8e932c3ee4d859
--- /dev/null
+++ b/src/account/actions.py
@@ -0,0 +1,100 @@
+
+import logging
+
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+
+import ldap
+from datetime import date
+from squarepay import dispense
+
+log = logging.getLogger('ldap')
+# load config
+
+ldap_uri = getattr(settings, 'AUTH_LDAP_SERVER_URI')
+ldap_search_dn = getattr(settings, 'REPLACE_ME')
+ldap_bind_dn = getattr()
+ldap_bind_secret = getattr()
+
+
+#initalise ldap instace
+_ldap_inst = ldap.initialize(ldap_uri)
+
+def get_ldap_instance():
+	return _ldap_inst
+
+def get_user_attrs(username, attrs):
+	# TODO verify bind
+	ld = get_ldap_instance()
+	filter = "cn=" + username
+	try:
+		result = ld.search_s(ldap_search_dn, ldap.SCOPE_SUBTREE, filter, attrs)
+		if result.size > 1:
+			# multiple accounts matched, this is a problem
+			return None
+		if result.size == 0:
+			# account does not exist
+			return None
+		return result[0]; 
+
+def get_account_lock_status(username):
+	ld = get_ldap_instance()
+	try:
+		ld.bind(ldap_bind_dn, ldap_bind_secret)
+		result = get_user_attrs(username, ['userAccountControl'])
+	finally:
+		ld.unbind()
+	return bool(result[1]['userAccountControl'] & 0x002)
+
+# locks the specified User Account by performing the following actions:
+# 1. set UAC ACCOUNTDISABLE flag (0x002) via ldap
+# 2. set user shell to `/etc/locked20xx` via ldap
+# 3. do `dispense user type disabled <username> <reason>`
+def lock_account(username):
+	# TODO: error handling
+	ld = get_ldap_instance()
+	today = date.today()
+	try:
+		ld.bind(ldap_bind_dn, ldap_bind_secret)
+		# fetch current uac
+		result = get_user_attrs(username, ['userAccountControl'])
+		
+		dn = result[0]
+		uac = result[1]['userAccountControl'] | 0x002 # set ACCOUNTDISABLE
+		actions = [
+			(ldap.MOD_REPLACE, "userAccountControl", uac),
+			(ldap.MOD_REPLACE, "userShell", "/etc/locked" + str(today.year))
+		]
+		# write record
+		ld.modify(dn, actions)
+
+	finally:
+		ld.unbind()
+	
+	reason = "account locked by uccportal on %s" % str(today)
+	dispense.set_dispense_flag(username, 'disabled', reason)
+
+def unlock_account(username):
+	# TODO: error handling
+	ld = get_ldap_instance()
+	today = date.today()
+	try:
+		ld.bind(ldap_bind_dn, ldap_bind_secret)
+		# fetch current uac
+		result = get_user_attrs(username, ['userAccountControl'])
+		
+		dn = result[0]
+		uac = result[1]['userAccountControl'] & ~0x002 # clear ACCOUNTDISABLE
+		actions = [
+			(ldap.MOD_REPLACE, "userAccountControl",uac),
+			(ldap.MOD_REPLACE, "userShell", "/bin/zsh")
+		]
+		# write record
+		ld.modify(dn, actions)
+
+	finally:
+		ld.unbind()
+	
+	reason = "account unlocked by uccportal on %s" % str(today)
+	dispense.set_dispense_flag(username, '!disabled', reason)
+
diff --git a/src/gms/wsgi.py b/src/gms/wsgi.py
deleted file mode 120000
index 398090eba8b8439bdd32ad2086d0f9090cfe9c32..0000000000000000000000000000000000000000
--- a/src/gms/wsgi.py
+++ /dev/null
@@ -1 +0,0 @@
-wsgi.wsgi
\ No newline at end of file
diff --git a/src/gms/wsgi.py b/src/gms/wsgi.py
new file mode 100644
index 0000000000000000000000000000000000000000..58d5ef649f8aa6b238968e0e94dcd07f61049f79
--- /dev/null
+++ b/src/gms/wsgi.py
@@ -0,0 +1,15 @@
+"""
+WSGI config for gms project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
+"""
+
+import os
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gms.settings")
+
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
diff --git a/src/memberdb/models.py b/src/memberdb/models.py
index 8dd7e3851b07750ce64ce39c297c2ed575385a4d..cfb8f33d508a147d1aa2dc4c7a9d6346879e4dd1 100644
--- a/src/memberdb/models.py
+++ b/src/memberdb/models.py
@@ -6,200 +6,211 @@ from django.urls import reverse
 
 from squarepay.dispense import get_item_price
 
+import ldap
+
 """
 dictionary of membership types & descriptions, should be updated if these are changed in dispense.
 """
 MEMBERSHIP_TYPES = {
-    'oday': {
-        'dispense':'pseudo:11',
-        'desc':'O\' Day Special - first time members only',
-        'is_guild':True,
-        'is_student':True,
-        'must_be_fresh':True,
-    },
-    'student_and_guild': {
-        'dispense':'pseudo:10',
-        'desc':'Student and UWA Guild member',
-        'is_guild':True,
-        'is_student':True,
-        'must_be_fresh':False,
-    },
-    'student_only': {
-        'dispense':'pseudo:9',
-        'desc':'Student and not UWA Guild member',
-        'is_guild':False,
-        'is_student':True,
-        'must_be_fresh':False,
-    },
-    'guild_only': {
-        'dispense':'pseudo:8',
-        'desc':'Non-Student and UWA Guild member',
-        'is_guild':True,
-        'is_student':False,
-        'must_be_fresh':False,
-    },
-    'non_student': {
-        'dispense':'pseudo:7',
-        'desc':'Non-Student and not UWA Guild member',
-        'is_guild':False,
-        'is_student':False,
-        'must_be_fresh':False,
-    },
-    'lifer': {
-        'dispense':'',
-        'desc':'Life member',
-        'is_guild':False,
-        'is_student':False,
-        'must_be_fresh':False,
-    }
+	'oday': {
+		'dispense':'pseudo:11',
+		'desc':'O\' Day Special - first time members only',
+		'is_guild':True,
+		'is_student':True,
+		'must_be_fresh':True,
+	},
+	'student_and_guild': {
+		'dispense':'pseudo:10',
+		'desc':'Student and UWA Guild member',
+		'is_guild':True,
+		'is_student':True,
+		'must_be_fresh':False,
+	},
+	'student_only': {
+		'dispense':'pseudo:9',
+		'desc':'Student and not UWA Guild member',
+		'is_guild':False,
+		'is_student':True,
+		'must_be_fresh':False,
+	},
+	'guild_only': {
+		'dispense':'pseudo:8',
+		'desc':'Non-Student and UWA Guild member',
+		'is_guild':True,
+		'is_student':False,
+		'must_be_fresh':False,
+	},
+	'non_student': {
+		'dispense':'pseudo:7',
+		'desc':'Non-Student and not UWA Guild member',
+		'is_guild':False,
+		'is_student':False,
+		'must_be_fresh':False,
+	},
+	'lifer': {
+		'dispense':'',
+		'desc':'Life member',
+		'is_guild':False,
+		'is_student':False,
+		'must_be_fresh':False,
+	}
 }
 
 def get_membership_choices(is_renew=None, get_prices=True):
-    """
-    turn MEMBERSHIP_TYPES into a list of choices used by Django
-    also dynamically fetch the prices from dispense (if possible)
-    """
-    choices = []
-    for key, val in MEMBERSHIP_TYPES.items():
-        if (val['must_be_fresh'] and is_renew == True):
-            # if you have an account already, you don't qualify for the fresher special
-            continue
-        if (val['dispense'] == '' and is_renew == False):
-            # free memberships can only apply to life members, and they will have an existing membership somewhere
-            # so this option is only displayed on the renewal form
-            continue
-        else:
-            if get_prices:
-                price = get_item_price(val['dispense'])
-            else:
-                price = None
-
-            if price is not None:
-                desc = "%s ($%1.2f)" % (val['desc'], price / 100.0)
-                choices += [(key, desc)]
-            else:
-                # don't display the price
-                choices += [(key, val['desc'])]
-
-    return choices
+	"""
+	turn MEMBERSHIP_TYPES into a list of choices used by Django
+	also dynamically fetch the prices from dispense (if possible)
+	"""
+	choices = []
+	for key, val in MEMBERSHIP_TYPES.items():
+		if (val['must_be_fresh'] and is_renew == True):
+			# if you have an account already, you don't qualify for the fresher special
+			continue
+		if (val['dispense'] == '' and is_renew == False):
+			# free memberships can only apply to life members, and they will have an existing membership somewhere
+			# so this option is only displayed on the renewal form
+			continue
+		else:
+			if get_prices:
+				price = get_item_price(val['dispense'])
+			else:
+				price = None
+
+			if price is not None:
+				desc = "%s ($%1.2f)" % (val['desc'], price / 100.0)
+				choices += [(key, desc)]
+			else:
+				# don't display the price
+				choices += [(key, val['desc'])]
+
+	return choices
 
 def get_membership_type(member):
-    best = 'non_student'
-    is_fresh = member.memberships.all().count() == 0
-    for i, t in MEMBERSHIP_TYPES.items():
-        if (t['must_be_fresh'] == is_fresh and t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
-            best = i
-            break
-        elif (t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
-            best = i
-            break
-    return best
+	best = 'non_student'
+	is_fresh = member.memberships.all().count() == 0
+	for i, t in MEMBERSHIP_TYPES.items():
+		if (t['must_be_fresh'] == is_fresh and t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
+			best = i
+			break
+		elif (t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
+			best = i
+			break
+	return best
 
 
 def make_token():
-    return get_random_string(128)
+	return get_random_string(128)
 
 PAYMENT_METHODS = [
-    ('dispense', 'Existing dispense credit'),
-    ('cash', 'Cash (in person)'),
-    ('card', 'Tap-n-Go via Square (in person)'),
-    ('online', 'Online payment via Square'),
-    ('eft', 'Bank transfer'),
-    ('', 'No payment')
+	('dispense', 'Existing dispense credit'),
+	('cash', 'Cash (in person)'),
+	('card', 'Tap-n-Go via Square (in person)'),
+	('online', 'Online payment via Square'),
+	('eft', 'Bank transfer'),
+	('', 'No payment')
 ]
 
+
+
+
 class IncAssocMember (models.Model):
-    """
-    Member record for data we are legally required to keep under Incorporations Act (and make available to members upon request)
-    Note: these data should only be changed administratively or with suitable validation since it must be up to date & accurate.
-    """
-
-    first_name      = models.CharField ('First name', max_length=200)
-    last_name       = models.CharField ('Surname', max_length=200)
-    email_address   = models.EmailField ('Email address', blank=False)
-    updated         = models.DateTimeField ('IncA. info last updated', auto_now=True)
-    created         = models.DateTimeField ('When created', auto_now_add=True)
-
-    def __str__ (self):
-        return "%s %s <%s>" % (self.first_name, self.last_name, self.email_address)
-    
-    class Meta:
-        verbose_name = "Incorporations Act member data"
-        verbose_name_plural = verbose_name
+	"""
+	Member record for data we are legally required to keep under Incorporations Act (and make available to members upon request)
+	Note: these data should only be changed administratively or with suitable validation since it must be up to date & accurate.
+	"""
+
+	first_name      = models.CharField ('First name', max_length=200)
+	last_name       = models.CharField ('Surname', max_length=200)
+	email_address   = models.EmailField ('Email address', blank=False)
+	updated         = models.DateTimeField ('IncA. info last updated', auto_now=True)
+	created         = models.DateTimeField ('When created', auto_now_add=True)
+
+	def __str__ (self):
+		return "%s %s <%s>" % (self.first_name, self.last_name, self.email_address)
+	
+	class Meta:
+		verbose_name = "Incorporations Act member data"
+		verbose_name_plural = verbose_name
 
 class Member (IncAssocMember):
-    """
-    Member table: only latest information, one record per member
-    Some of this data may be required by the UWA Student Guild. Other stuff is just good to know,
-    and we don't _need_ to keep historical data for every current/past member.
-    Note: Privacy laws are a thing, unless people allow it then we cannot provide this info to members.
-    """
-
-    # data to be entered by user and validated (mostly) manually
-    display_name    = models.CharField ('Display name', max_length=200)
-    username        = models.SlugField ('Username', max_length=32, null=False, blank=False, unique=True, validators=[RegexValidator(regex='^[a-z0-9._-]+$')])
-    phone_number    = models.CharField ('Phone number', max_length=20, blank=False, validators=[RegexValidator(regex='^\+?[0-9() -]+$')])
-    is_student      = models.BooleanField ('Student', default=True, blank=True, help_text="Tick this box if you are a current student at a secondary or tertiary institution in WA")
-    is_guild        = models.BooleanField ('UWA Guild member', default=True, blank=True)
-    id_number       = models.CharField ('Student email or Drivers License', max_length=255, blank=False, help_text="Student emails should end with '@student.*.edu.au' and drivers licences should be in the format '<AU state> 1234567'")
-
-    # data used internally by the system, not to be touched, seen or heard (except when it is)
-    member_updated  = models.DateTimeField ('Internal UCC info last updated', auto_now=True)
-    login_token     = models.CharField ('Temporary access key', max_length=128, null=True, editable=False, default=make_token)
-    email_confirm   = models.BooleanField ('Email address confirmed', null=False, editable=False, default=False)
-    studnt_confirm  = models.BooleanField ('Student status confirmed', null=False, editable=False, default=False)
-    guild_confirm   = models.BooleanField ('Guild status confirmed', null=False, editable=False, default=False)
-
-    def __str__ (self):
-        if (self.display_name != "%s %s" % (self.first_name, self.last_name)):
-            name = "%s (%s %s)" % (self.display_name, self.first_name, self.last_name)
-        else:
-            name = self.display_name
-        return "[%s] %s" % (self.username, name)
-
-    class Meta:
-        verbose_name = "Internal UCC member record"
+	"""
+	Member table: only latest information, one record per member
+	Some of this data may be required by the UWA Student Guild. Other stuff is just good to know,
+	and we don't _need_ to keep historical data for every current/past member.
+	Note: Privacy laws are a thing, unless people allow it then we cannot provide this info to members.
+	"""
+
+	# data to be entered by user and validated (mostly) manually
+	display_name    = models.CharField ('Display name', max_length=200)
+	username        = models.SlugField ('Username', max_length=32, null=False, blank=False, unique=True, validators=[RegexValidator(regex='^[a-z0-9._-]+$')])
+	phone_number    = models.CharField ('Phone number', max_length=20, blank=False, validators=[RegexValidator(regex='^\+?[0-9() -]+$')])
+	is_student      = models.BooleanField ('Student', default=True, blank=True, help_text="Tick this box if you are a current student at a secondary or tertiary institution in WA")
+	is_guild        = models.BooleanField ('UWA Guild member', default=True, blank=True)
+	id_number       = models.CharField ('Student email or Drivers License', max_length=255, blank=False, help_text="Student emails should end with '@student.*.edu.au' and drivers licences should be in the format '<AU state> 1234567'")
+
+	# data used internally by the system, not to be touched, seen or heard (except when it is)
+	member_updated  = models.DateTimeField ('Internal UCC info last updated', auto_now=True)
+	login_token     = models.CharField ('Temporary access key', max_length=128, null=True, editable=False, default=make_token)
+	email_confirm   = models.BooleanField ('Email address confirmed', null=False, editable=False, default=False)
+	studnt_confirm  = models.BooleanField ('Student status confirmed', null=False, editable=False, default=False)
+	guild_confirm   = models.BooleanField ('Guild status confirmed', null=False, editable=False, default=False)
+
+	# account info
+	def get_account_status(self):
+		
+			
+		return;
+
+	def __str__ (self):
+		if (self.display_name != "%s %s" % (self.first_name, self.last_name)):
+			name = "%s (%s %s)" % (self.display_name, self.first_name, self.last_name)
+		else:
+			name = self.display_name
+		return "[%s] %s" % (self.username, name)
+
+	class Meta:
+		verbose_name = "Internal UCC member record"
 
 class Membership (models.Model):
-    """
-    Membership table: store information related to individual (successful/accepted) signups/renewals
-    """
+	"""
+	Membership table: store information related to individual (successful/accepted) signups/renewals
+	"""
 
-    member          = models.ForeignKey (Member, on_delete=models.CASCADE, related_name='memberships')
-    membership_type = models.CharField ('Membership type', max_length=20, blank=True, null=False, choices=get_membership_choices(get_prices=False))
-    payment_method  = models.CharField ('Payment method', max_length=10, blank=True, null=True, choices=PAYMENT_METHODS, default=None)
-    approved        = models.BooleanField ('Membership approved', default=False)
-    approver        = models.ForeignKey (Member, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_memberships')
-    date_submitted  = models.DateTimeField ('Date signed up')
-    date_paid       = models.DateTimeField ('Date of payment', blank=True, null=True)
-    date_approved   = models.DateTimeField ('Date approved', blank=True, null=True)
+	member          = models.ForeignKey (Member, on_delete=models.CASCADE, related_name='memberships')
+	membership_type = models.CharField ('Membership type', max_length=20, blank=True, null=False, choices=get_membership_choices(get_prices=False))
+	payment_method  = models.CharField ('Payment method', max_length=10, blank=True, null=True, choices=PAYMENT_METHODS, default=None)
+	approved        = models.BooleanField ('Membership approved', default=False)
+	approver        = models.ForeignKey (Member, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_memberships')
+	date_submitted  = models.DateTimeField ('Date signed up')
+	date_paid       = models.DateTimeField ('Date of payment', blank=True, null=True)
+	date_approved   = models.DateTimeField ('Date approved', blank=True, null=True)
 
-    def __str__ (self):
-        return "Member [%s] (%s) renewed membership on %s" % (self.member.username, self.member.display_name, self.date_submitted.strftime("%Y-%m-%d"))
+	def __str__ (self):
+		return "Member [%s] (%s) renewed membership on %s" % (self.member.username, self.member.display_name, self.date_submitted.strftime("%Y-%m-%d"))
 
-    def get_dispense_item(self):
-        return MEMBERSHIP_TYPES[self.membership_type]['dispense']
+	def get_dispense_item(self):
+		return MEMBERSHIP_TYPES[self.membership_type]['dispense']
 
-    class Meta:
-        verbose_name = "Membership renewal record"
-        ordering = ['approved', '-date_submitted']
+	class Meta:
+		verbose_name = "Membership renewal record"
+		ordering = ['approved', '-date_submitted']
 
 class TokenConfirmation(models.Model):
-    """ keep track of email confirmation tokens etc. and which field to update """
-    member          = models.ForeignKey (Member, on_delete=models.CASCADE, related_name='token_confirmations')
-    confirm_token   = models.CharField ('unique confirmation URL token', max_length=128, null=False, default=make_token)
-    model_field     = models.CharField ('name of BooleanField to update on parent when confirmed', max_length=40, null=False, blank=False)
-    created         = models.DateTimeField ('creation date', auto_now_add=True)
-
-    def mark_confirmed(self):
-        """ try to mark as confirmed, if error then silently fail """
-        try:
-            m = self.member
-            setattr(m, self.model_field)
-            m.save()
-            self.delete()
-        except Member.DoesNotExist as e:
-            pass
-
-    def get_absolute_url(self):
-        return reverse('memberdb:email_confirm', kwargs={'pk': self.id, 'token': self.confirm_token})
+	""" keep track of email confirmation tokens etc. and which field to update """
+	member          = models.ForeignKey (Member, on_delete=models.CASCADE, related_name='token_confirmations')
+	confirm_token   = models.CharField ('unique confirmation URL token', max_length=128, null=False, default=make_token)
+	model_field     = models.CharField ('name of BooleanField to update on parent when confirmed', max_length=40, null=False, blank=False)
+	created         = models.DateTimeField ('creation date', auto_now_add=True)
+
+	def mark_confirmed(self):
+		""" try to mark as confirmed, if error then silently fail """
+		try:
+			m = self.member
+			setattr(m, self.model_field)
+			m.save()
+			self.delete()
+		except Member.DoesNotExist as e:
+			pass
+
+	def get_absolute_url(self):
+		return reverse('memberdb:email_confirm', kwargs={'pk': self.id, 'token': self.confirm_token})
diff --git a/src/squarepay/dispense.py b/src/squarepay/dispense.py
index c0ffdfd932f8ae578e47d24de345cfd979f82d16..9cd4d089f73223583b637ccba4004e137ee5be2d 100644
--- a/src/squarepay/dispense.py
+++ b/src/squarepay/dispense.py
@@ -12,40 +12,54 @@ from .payments import log
 DISPENSE_BIN = getattr(settings, 'DISPENSE_BIN', None)
 
 if DISPENSE_BIN is None:
-    log.warning("DISPENSE_BIN is not defined! Lookups for prices will fallback to weird numbers (for testing)!")
+	log.warning("DISPENSE_BIN is not defined! Lookups for prices will fallback to weird numbers (for testing)!")
 
 def run_dispense(*args):
-    if DISPENSE_BIN is None:
-        return None
-    
-    cmd = [DISPENSE_BIN] + args
-    log.info("run_dispense: " + cmd)
-    try:
-        # get a string containing the output of the program
-        res = subprocess.check_output(cmd, timeout=4, universal_newlines=True)
-    except CalledProcessError as e:
-        log.warning("dispense returned error code %d, output: '%s'" % (e.returncode, e.output))
-        return None
-    except TimeoutExpired as e:
-        log.error(e)
-        return None
-    return res
+	if DISPENSE_BIN is None:
+		return None
+	
+	cmd = [DISPENSE_BIN] + args
+	log.info("run_dispense: " + cmd)
+	try:
+		# get a string containing the output of the program
+		res = subprocess.check_output(cmd, timeout=4, universal_newlines=True)
+	except CalledProcessError as e:
+		log.warning("dispense returned error code %d, output: '%s'" % (e.returncode, e.output))
+		return None
+	except TimeoutExpired as e:
+		log.error(e)
+		return None
+	return res
 
 def get_item_price(itemid):
-    """ gets the price of the given dispense item in cents """
-    if (itemid is None or itemid == ""):
-        return None
-    if DISPENSE_BIN is None:
-        return 2223
-
-    out = run_dispense('iteminfo', itemid)
-    if out is None:
-        return None
-    
-    s = out.split() # get something like ['pseudo:7', '25.00', 'membership', '(non-student', 'and', 'non-guild)']
-    if (s[0] != itemid):
-        log.warning("get_item_price: got result for incorrect item: %s" + s)
-        return None
-    else:
-        # return the price as a number of cents
-        return int(float(s[0]) * 100)
+	""" gets the price of the given dispense item in cents """
+	if (itemid is None or itemid == ""):
+		return None
+	if DISPENSE_BIN is None:
+		return 2223
+
+	out = run_dispense('iteminfo', itemid)
+	if out is None:
+		return None
+	
+	s = out.split() # get something like ['pseudo:7', '25.00', 'membership', '(non-student', 'and', 'non-guild)']
+	if (s[0] != itemid):
+		log.warning("get_item_price: got result for incorrect item: %s" + s)
+		return None
+	else:
+		# return the price as a number of cents
+		return int(float(s[0]) * 100)
+
+def set_dispense_flag(user, flag, reason):
+	if DISPENSE_BIN is None:
+		log.warning("DISPENSE_BIN is not defined, user will not be disabled")
+		return False
+
+	cmd = [DISPENSE_BIN] + args
+	out = run_dispense('user', 'type', user, flag, reason)
+	s = out.split()
+	if s[2] != "updated":
+		# user was not updated
+		log.warning("set_dispense_flag: user was not updated with error: " + out)
+		return False;
+	return True;
diff --git a/src/templates/admin/memberdb/membership_actions.html b/src/templates/admin/memberdb/membership_actions.html
index 6073fe9412c12194dc85e258f034a478713d78a6..4d5c55b6260f06697a7b52f2e1f54462d599a608 100644
--- a/src/templates/admin/memberdb/membership_actions.html
+++ b/src/templates/admin/memberdb/membership_actions.html
@@ -4,4 +4,5 @@
     {% if not ms.approved %}
     <a class="button" href="{{ member_approve }}">Approve</a>&nbsp;
     {% endif %}
+    <a class="button" href="{{ account_create }}">Create Account</a>&nbsp;
 </div>