diff --git a/ietf/checks.py b/ietf/checks.py
index 9f59d36c6..53e695a76 100644
--- a/ietf/checks.py
+++ b/ietf/checks.py
@@ -1,4 +1,4 @@
-# Copyright The IETF Trust 2015-2020, All Rights Reserved
+# Copyright The IETF Trust 2015-2021, All Rights Reserved
 # -*- coding: utf-8 -*-
 
 
@@ -62,14 +62,14 @@ def check_group_email_aliases_exists(app_configs, **kwargs):
         if not ok:
             errors.append(checks.Error(
                 "Found no aliases in the group email aliases file\n'%s'."%settings.GROUP_ALIASES_PATH,
-                hint="Please run ietf/bin/generate-wg-aliases to generate them.",
+                hint="Please run the generate_group_aliases management command to generate them.",
                 obj=None,
                 id="datatracker.E0002",
             ))
     except IOError as e:
         errors.append(checks.Error(
             "Could not read group email aliases:\n   %s" % e,
-            hint="Please run ietf/bin/generate-wg-aliases to generate them.",
+            hint="Please run the generate_group_aliases management command to generate them.",
             obj=None,
             id="datatracker.E0003",
         ))
@@ -89,14 +89,14 @@ def check_doc_email_aliases_exists(app_configs, **kwargs):
         if not ok:
             errors.append(checks.Error(
                 "Found no aliases in the document email aliases file\n'%s'."%settings.DRAFT_VIRTUAL_PATH,
-                hint="Please run ietf/bin/generate-draft-aliases to generate them.",
+                hint="Please run the generate_draft_aliases management command to generate them.",
                 obj=None,
                 id="datatracker.E0004",
             ))
     except IOError as e:
         errors.append(checks.Error(
             "Could not read document email aliases:\n   %s" % e,
-            hint="Please run ietf/bin/generate-draft-aliases to generate them.",
+            hint="Please run the generate_draft_aliases management command to generate them.",
             obj=None,
             id="datatracker.E0005",
         ))
diff --git a/ietf/doc/management/commands/generate_draft_aliases.py b/ietf/doc/management/commands/generate_draft_aliases.py
new file mode 100755
index 000000000..a16805ba4
--- /dev/null
+++ b/ietf/doc/management/commands/generate_draft_aliases.py
@@ -0,0 +1,163 @@
+# Copyright The IETF Trust 2012-2021, All Rights Reserved
+# -*- coding: utf-8 -*-
+
+# This was written as a script by Markus Stenberg <markus.stenberg@iki.fi>.
+# It was turned into a management command by Russ Housley <housley@vigisec.com>.
+
+import datetime
+import io
+import os
+import re
+import time
+    
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+import debug                            # pyflakes:ignore
+
+from ietf.doc.models import Document
+from ietf.group.utils import get_group_role_emails, get_group_ad_emails
+from ietf.utils.aliases import dump_sublist
+from utils.mail import parseaddr
+
+DEFAULT_YEARS = 2
+
+
+def get_draft_ad_emails(doc):
+    """Get AD email addresses for the given draft, if any."""
+    ad_emails = set()
+    # If working group document, return current WG ADs
+    if doc.group and doc.group.acronym != 'none':
+        ad_emails.update(get_group_ad_emails(doc.group))
+    # Document may have an explicit AD set
+    if doc.ad:
+        ad_emails.add(doc.ad.email_address())
+    return ad_emails
+
+
+def get_draft_chair_emails(doc):
+    """Get chair email addresses for the given draft, if any."""
+    chair_emails = set()
+    if doc.group:
+        chair_emails.update(get_group_role_emails(doc.group, ['chair', 'secr']))
+    return chair_emails
+
+
+def get_draft_shepherd_email(doc):
+    """Get shepherd email addresses for the given draft, if any."""
+    shepherd_email = set()
+    if doc.shepherd:
+        shepherd_email.add(doc.shepherd.email_address())
+    return shepherd_email
+
+
+def get_draft_authors_emails(doc):
+    """Get list of authors for the given draft."""
+    author_emails = set()
+    for author in doc.documentauthor_set.all():
+        if author.email and author.email.email_address():
+            author_emails.add(author.email.email_address())
+    return author_emails
+
+
+def get_draft_notify_emails(doc):
+    """Get list of email addresses to notify for the given draft."""
+    ad_email_alias_regex = r"^%s.ad@(%s|%s)$" % (doc.name, settings.DRAFT_ALIAS_DOMAIN, settings.TOOLS_SERVER)
+    all_email_alias_regex = r"^%s.all@(%s|%s)$" % (doc.name, settings.DRAFT_ALIAS_DOMAIN, settings.TOOLS_SERVER)
+    author_email_alias_regex = r"^%s@(%s|%s)$" % (doc.name, settings.DRAFT_ALIAS_DOMAIN, settings.TOOLS_SERVER)
+    notify_email_alias_regex = r"^%s.notify@(%s|%s)$" % (doc.name, settings.DRAFT_ALIAS_DOMAIN, settings.TOOLS_SERVER)
+    shepherd_email_alias_regex = r"^%s.shepherd@(%s|%s)$" % (doc.name, settings.DRAFT_ALIAS_DOMAIN, settings.TOOLS_SERVER)
+    notify_emails = set()
+    if doc.notify:
+        for e in doc.notify.split(','):
+            e = e.strip()
+            if re.search(ad_email_alias_regex, e):
+                notify_emails.update(get_draft_ad_emails(doc))
+            elif re.search(author_email_alias_regex, e):
+                notify_emails.update(get_draft_authors_emails(doc))
+            elif re.search(shepherd_email_alias_regex, e):
+                notify_emails.update(get_draft_shepherd_email(doc))
+            elif re.search(all_email_alias_regex, e):
+                notify_emails.update(get_draft_ad_emails(doc))
+                notify_emails.update(get_draft_authors_emails(doc))
+                notify_emails.update(get_draft_shepherd_email(doc))
+            elif re.search(notify_email_alias_regex, e):
+                pass
+            else:
+                (name, email) = parseaddr(e)
+                notify_emails.add(email)
+    return notify_emails
+
+
+class Command(BaseCommand):
+    help = ('Generate the draft-aliases and draft-virtual files for Internet-Draft '
+            'mail aliases, placing them in the files configured in '
+            'settings.DRAFT_ALIASES_PATH and settings.DRAFT_VIRTUAL_PATH, '
+            'respectively.  The generation includes aliases for Internet-Drafts '
+            'that have seen activity in the last %s years.' % (DEFAULT_YEARS))
+
+    def handle(self, *args, **options):
+        show_since = datetime.datetime.now() - datetime.timedelta(DEFAULT_YEARS*365)
+
+        date = time.strftime("%Y-%m-%d_%H:%M:%S")
+        signature = '# Generated by %s at %s\n' % (os.path.abspath(__file__), date)
+
+        afile = io.open(settings.DRAFT_ALIASES_PATH, "w")
+        vfile = io.open(settings.DRAFT_VIRTUAL_PATH, "w")
+
+        afile.write(signature)
+        vfile.write(signature)
+        vfile.write("%s anything\n" % settings.DRAFT_VIRTUAL_DOMAIN)
+
+        # Internet-Drafts with active status or expired within DEFAULT_YEARS
+        drafts = Document.objects.filter(name__startswith='draft-')
+        active_drafts = drafts.filter(states__slug='active')
+        inactive_recent_drafts = drafts.exclude(states__slug='active').filter(expires__gte=show_since)
+        interesting_drafts = active_drafts | inactive_recent_drafts
+
+        alias_domains = ['ietf.org', ]
+        for draft in interesting_drafts.distinct().iterator():
+            # Omit RFCs, unless they were published in the last DEFAULT_YEARS
+            if draft.docalias.filter(name__startswith='rfc'):
+                if draft.latest_event(type='published_rfc').time < show_since:
+                    continue
+
+            alias = draft.name
+            all = set()
+
+            # no suffix and .authors are the same list
+            emails = get_draft_authors_emails(draft)
+            all.update(emails)
+            dump_sublist(afile, vfile, alias, alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, emails)
+            dump_sublist(afile, vfile, alias+'.authors', alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, emails)
+
+            # .chairs = group chairs
+            emails = get_draft_chair_emails(draft)
+            if emails:
+                all.update(emails)
+                dump_sublist(afile, vfile, alias+'.chairs', alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, emails)
+
+            # .ad = sponsoring AD / WG AD (WG document)
+            emails = get_draft_ad_emails(draft)
+            if emails:
+                all.update(emails)
+                dump_sublist(afile, vfile, alias+'.ad', alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, emails)
+
+            # .notify = notify email list from the Document
+            emails = get_draft_notify_emails(draft)
+            if emails:
+                all.update(emails)
+                dump_sublist(afile, vfile, alias+'.notify', alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, emails)
+
+            # .shepherd = shepherd email from the Document
+            emails = get_draft_shepherd_email(draft)
+            if emails:
+                all.update(emails)
+                dump_sublist(afile, vfile, alias+'.shepherd', alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, emails)
+
+            # .all = everything from above
+            dump_sublist(afile, vfile, alias+'.all', alias_domains, settings.DRAFT_VIRTUAL_DOMAIN, all)
+
+        afile.close()
+        vfile.close()
+    
\ No newline at end of file
diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py
index fca0318d0..b5aeaf203 100644
--- a/ietf/doc/tests.py
+++ b/ietf/doc/tests.py
@@ -10,12 +10,12 @@ import lxml
 import bibtexparser
 import mock
 
-
 from http.cookies import SimpleCookie
 from pyquery import PyQuery
 from urllib.parse import urlparse, parse_qs
 from tempfile import NamedTemporaryFile
 
+from django.core.management import call_command
 from django.urls import reverse as urlreverse
 from django.conf import settings
 
@@ -1379,7 +1379,123 @@ class ReferencesTest(TestCase):
         r = self.client.get(url)
         self.assertEqual(r.status_code, 200)
         self.assertContains(r, doc1.name)
-       
+
+class GenerateDraftAliasesTests(TestCase):
+   def setUp(self):
+       self.doc_aliases_file = NamedTemporaryFile(delete=False, mode='w+')
+       self.doc_aliases_file.close()
+       self.doc_virtual_file = NamedTemporaryFile(delete=False, mode='w+')
+       self.doc_virtual_file.close()
+       self.saved_draft_aliases_path = settings.DRAFT_ALIASES_PATH
+       self.saved_draft_virtual_path = settings.DRAFT_VIRTUAL_PATH
+       settings.DRAFT_ALIASES_PATH = self.doc_aliases_file.name
+       settings.DRAFT_VIRTUAL_PATH = self.doc_virtual_file.name
+
+   def tearDown(self):
+       settings.DRAFT_ALIASES_PATH = self.saved_draft_aliases_path
+       settings.DRAFT_VIRTUAL_PATH = self.saved_draft_virtual_path
+       os.unlink(self.doc_aliases_file.name)
+       os.unlink(self.doc_virtual_file.name)
+
+   def testManagementCommand(self):
+       a_month_ago = datetime.datetime.now() - datetime.timedelta(30)
+       ad = RoleFactory(name_id='ad', group__type_id='area', group__state_id='active').person
+       shepherd = PersonFactory()
+       author1 = PersonFactory()
+       author2 = PersonFactory()
+       author3 = PersonFactory()
+       author4 = PersonFactory()
+       author5 = PersonFactory()
+       author6 = PersonFactory()
+       mars = GroupFactory(type_id='wg', acronym='mars')
+       marschairman = PersonFactory(user__username='marschairman')
+       mars.role_set.create(name_id='chair', person=marschairman, email=marschairman.email())
+       doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad)
+       doc2 = WgDraftFactory(name='draft-ietf-mars-test', group__acronym='mars', authors=[author2], ad=ad)
+       doc3 = WgRfcFactory.create(name='draft-ietf-mars-finished', group__acronym='mars', authors=[author3], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=a_month_ago)
+       DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago.strftime("%Y-%m-%d"))
+       doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10))
+       DocEventFactory.create(doc=doc4, type='published_rfc', time = '2010-10-10')
+       doc5 = IndividualDraftFactory(authors=[author6])
+
+       args = [ ]
+       kwargs = { }
+       out = io.StringIO()
+       call_command("generate_draft_aliases", *args, **kwargs, stdout=out, stderr=out)
+       self.assertFalse(out.getvalue())
+
+       with open(settings.DRAFT_ALIASES_PATH) as afile:
+           acontent = afile.read()
+           self.assertTrue(all([x in acontent for x in [
+               'xfilter-' + doc1.name,
+               'xfilter-' + doc1.name + '.ad',
+               'xfilter-' + doc1.name + '.authors',
+               'xfilter-' + doc1.name + '.shepherd',
+               'xfilter-' + doc1.name + '.all',
+               'xfilter-' + doc2.name,
+               'xfilter-' + doc2.name + '.ad',
+               'xfilter-' + doc2.name + '.authors',
+               'xfilter-' + doc2.name + '.chairs',
+               'xfilter-' + doc2.name + '.all',
+               'xfilter-' + doc3.name,
+               'xfilter-' + doc3.name + '.ad',
+               'xfilter-' + doc3.name + '.authors',
+               'xfilter-' + doc3.name + '.chairs',
+               'xfilter-' + doc5.name,
+               'xfilter-' + doc5.name + '.authors',
+               'xfilter-' + doc5.name + '.all',
+           ]]))
+           self.assertFalse(all([x in acontent for x in [
+               'xfilter-' + doc1.name + '.chairs',
+               'xfilter-' + doc2.name + '.shepherd',
+               'xfilter-' + doc3.name + '.shepherd',
+               'xfilter-' + doc4.name,
+               'xfilter-' + doc5.name + '.shepherd',
+               'xfilter-' + doc5.name + '.ad',
+           ]]))
+
+       with open(settings.DRAFT_VIRTUAL_PATH) as vfile:
+           vcontent = vfile.read()
+           self.assertTrue(all([x in vcontent for x in [
+               ad.email_address(),
+               shepherd.email_address(),
+               marschairman.email_address(),
+               author1.email_address(),
+               author2.email_address(),
+               author3.email_address(),
+               author6.email_address(),
+           ]]))
+           self.assertFalse(all([x in vcontent for x in [
+               author4.email_address(),
+               author5.email_address(),
+           ]]))
+           self.assertTrue(all([x in vcontent for x in [
+               'xfilter-' + doc1.name,
+               'xfilter-' + doc1.name + '.ad',
+               'xfilter-' + doc1.name + '.authors',
+               'xfilter-' + doc1.name + '.shepherd',
+               'xfilter-' + doc1.name + '.all',
+               'xfilter-' + doc2.name,
+               'xfilter-' + doc2.name + '.ad',
+               'xfilter-' + doc2.name + '.authors',
+               'xfilter-' + doc2.name + '.chairs',
+               'xfilter-' + doc2.name + '.all',
+               'xfilter-' + doc3.name,
+               'xfilter-' + doc3.name + '.ad',
+               'xfilter-' + doc3.name + '.authors',
+               'xfilter-' + doc3.name + '.chairs',
+               'xfilter-' + doc5.name,
+               'xfilter-' + doc5.name + '.authors',
+               'xfilter-' + doc5.name + '.all',
+           ]]))
+           self.assertFalse(all([x in vcontent for x in [
+               'xfilter-' + doc1.name + '.chairs',
+               'xfilter-' + doc2.name + '.shepherd',
+               'xfilter-' + doc3.name + '.shepherd',
+               'xfilter-' + doc4.name,
+               'xfilter-' + doc5.name + '.shepherd',
+               'xfilter-' + doc5.name + '.ad',
+           ]]))
 
 class EmailAliasesTests(TestCase):
 
diff --git a/ietf/group/management/commands/generate_group_aliases.py b/ietf/group/management/commands/generate_group_aliases.py
new file mode 100755
index 000000000..7ce4da4b8
--- /dev/null
+++ b/ietf/group/management/commands/generate_group_aliases.py
@@ -0,0 +1,80 @@
+# Copyright The IETF Trust 2012-2021, All Rights Reserved
+# -*- coding: utf-8 -*-
+
+# This was written as a script by Markus Stenberg <markus.stenberg@iki.fi>.
+# It was turned into a management command by Russ Housley <housley@vigisec.com>.
+
+import datetime
+import io
+import os
+import time
+    
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+import debug                            # pyflakes:ignore
+
+from ietf.group.models import Group
+from ietf.group.utils import get_group_ad_emails, get_group_role_emails, get_child_group_role_emails
+from ietf.name.models import GroupTypeName
+from ietf.utils.aliases import dump_sublist
+
+DEFAULT_YEARS = 5
+ACTIVE_STATES=['active', 'bof', 'proposed']
+GROUP_TYPES=['wg','rg','dir','team','review','program']
+NO_AD_GROUP_TYPES=['rg','team','program']
+
+class Command(BaseCommand):
+    help = ('Generate the group-aliases and group-virtual files for Internet-Draft '
+            'mail aliases, placing them in the file configured in '
+            'settings.GROUP_ALIASES_PATH and settings.GROUP_VIRTUAL_PATH, '
+            'respectively.  The generation includes aliases for groups that '
+            'have seen activity in the last %s years.' % (DEFAULT_YEARS))
+
+    def handle(self, *args, **options):
+        show_since = datetime.datetime.now() - datetime.timedelta(DEFAULT_YEARS*365)
+
+        date = time.strftime("%Y-%m-%d_%H:%M:%S")
+        signature = '# Generated by %s at %s\n' % (os.path.abspath(__file__), date)
+
+        afile = io.open(settings.GROUP_ALIASES_PATH, "w")
+        vfile = io.open(settings.GROUP_VIRTUAL_PATH, "w")
+
+        afile.write(signature)
+        vfile.write(signature)
+        vfile.write("%s anything\n" % settings.GROUP_VIRTUAL_DOMAIN)
+
+        alias_domains = ['ietf.org', ]
+
+        # Loop through each group type and build -ads and -chairs entries
+        for g in GROUP_TYPES:
+            entries = Group.objects.filter(type=g).all()
+            active_entries = entries.filter(state__in=ACTIVE_STATES)
+            inactive_recent_entries = entries.exclude(state__in=ACTIVE_STATES).filter(time__gte=show_since)
+            interesting_entries = active_entries | inactive_recent_entries
+
+            for e in interesting_entries.distinct().iterator():
+                name = e.acronym
+                # Research groups, teams, and programs do not have -ads lists
+                if not g in NO_AD_GROUP_TYPES:
+                    dump_sublist(afile, vfile, name+'-ads', ['ietf.org', ], settings.GROUP_VIRTUAL_DOMAIN, get_group_ad_emails(e))
+                # All group types have -chairs lists
+                dump_sublist(afile, vfile, name+'-chairs', ['ietf.org', ], settings.GROUP_VIRTUAL_DOMAIN, get_group_role_emails(e, ['chair', 'secr']))
+
+        # The area lists include every chair in active working groups in the area
+        areas = Group.objects.filter(type='area').all()
+        active_areas = areas.filter(state__in=ACTIVE_STATES)
+        for area in active_areas:
+            name = area.acronym
+            area_ad_emails = get_group_role_emails(area, ['pre-ad', 'ad', 'chair'])
+            dump_sublist(afile, vfile, name+'-ads', alias_domains, settings.GROUP_VIRTUAL_DOMAIN, area_ad_emails)
+            dump_sublist(afile, vfile, name+'-chairs', alias_domains, settings.GROUP_VIRTUAL_DOMAIN, (get_child_group_role_emails(area, ['chair', 'secr']) | area_ad_emails))
+
+        # Other groups with chairs that require Internet-Draft submission approval
+        gtypes = GroupTypeName.objects.values_list('slug', flat=True)
+        special_groups = Group.objects.filter(type__features__req_subm_approval=True, acronym__in=gtypes, state='active')
+        for group in special_groups:
+            dump_sublist(afile, vfile, group.acronym+'-chairs', alias_domains, settings.GROUP_VIRTUAL_DOMAIN, get_group_role_emails(group, ['chair', 'delegate']))
+
+        afile.close()
+        vfile.close()
\ No newline at end of file
diff --git a/ietf/group/tests.py b/ietf/group/tests.py
index 493a49c09..398969038 100644
--- a/ietf/group/tests.py
+++ b/ietf/group/tests.py
@@ -1,10 +1,14 @@
 # Copyright The IETF Trust 2013-2020, All Rights Reserved
 # -*- coding: utf-8 -*-
 
-
+import io
 import os
-from unittest import skipIf
+import datetime
 
+from unittest import skipIf
+from tempfile import NamedTemporaryFile
+
+from django.core.management import call_command
 from django.conf import settings
 from django.urls import reverse as urlreverse
 from django.db.models import Q
@@ -18,10 +22,11 @@ from ietf.group.models import Role, Group
 from ietf.group.utils import get_group_role_emails, get_child_group_role_emails, get_group_ad_emails
 from ietf.group.factories import GroupFactory, RoleFactory
 from ietf.utils.test_runner import set_coverage_checking
-from ietf.person.factories import EmailFactory
+from ietf.person.factories import PersonFactory, EmailFactory
 from ietf.person.models import Person
 from ietf.utils.test_utils import login_testing_unauthorized, TestCase
 
+
 if   getattr(settings,'SKIP_DOT_TO_PDF', False):
     skip_dot_to_pdf = True
     skip_message = "settings.SKIP_DOT_TO_PDF = %s" % skip_dot_to_pdf
@@ -116,6 +121,111 @@ class GroupDocDependencyGraphTests(TestCase):
                     "%s has no content"%group.acronym)
             
 
+class GenerateGroupAliasesTests(TestCase):
+    def setUp(self):
+        self.doc_aliases_file = NamedTemporaryFile(delete=False, mode='w+')
+        self.doc_aliases_file.close()
+        self.doc_virtual_file = NamedTemporaryFile(delete=False, mode='w+')
+        self.doc_virtual_file.close()
+        self.saved_draft_aliases_path = settings.GROUP_ALIASES_PATH
+        self.saved_draft_virtual_path = settings.GROUP_VIRTUAL_PATH
+        settings.GROUP_ALIASES_PATH = self.doc_aliases_file.name
+        settings.GROUP_VIRTUAL_PATH = self.doc_virtual_file.name
+        
+    def tearDown(self):
+        settings.GROUP_ALIASES_PATH = self.saved_draft_aliases_path
+        settings.GROUP_VIRTUAL_PATH = self.saved_draft_virtual_path
+        os.unlink(self.doc_aliases_file.name)
+        os.unlink(self.doc_virtual_file.name)
+
+    def testManagementCommand(self):
+        a_month_ago = datetime.datetime.now() - datetime.timedelta(30)
+        a_decade_ago = datetime.datetime.now() - datetime.timedelta(3650)
+        role1 = RoleFactory(name_id='ad', group__type_id='area', group__acronym='myth', group__state_id='active')
+        area = role1.group
+        ad = role1.person
+        mars = GroupFactory(type_id='wg', acronym='mars', parent=area)
+        marschair = PersonFactory(user__username='marschair')
+        mars.role_set.create(name_id='chair', person=marschair, email=marschair.email())
+        marssecr = PersonFactory(user__username='marssecr')
+        mars.role_set.create(name_id='secr', person=marssecr, email=marssecr.email())
+        ames = GroupFactory(type_id='wg', acronym='ames', parent=area)
+        ameschair = PersonFactory(user__username='ameschair')
+        ames.role_set.create(name_id='chair', person=ameschair, email=ameschair.email())
+        recent = GroupFactory(type_id='wg', acronym='recent', parent=area, state_id='conclude', time=a_month_ago)
+        recentchair = PersonFactory(user__username='recentchair')
+        recent.role_set.create(name_id='chair', person=recentchair, email=recentchair.email())
+        wayold = GroupFactory(type_id='wg', acronym='recent', parent=area, state_id='conclude', time=a_decade_ago)
+        wayoldchair = PersonFactory(user__username='wayoldchair')
+        wayold.role_set.create(name_id='chair', person=wayoldchair, email=wayoldchair.email())
+        role2 = RoleFactory(name_id='ad', group__type_id='area', group__acronym='done', group__state_id='conclude')
+        done = role2.group
+        done_ad = role2.person
+        individual = PersonFactory()
+
+        args = [ ]
+        kwargs = { }
+        out = io.StringIO()
+        call_command("generate_group_aliases", *args, **kwargs, stdout=out, stderr=out)
+        self.assertFalse(out.getvalue())
+
+        with open(settings.GROUP_ALIASES_PATH) as afile:
+            acontent = afile.read()
+            self.assertTrue('xfilter-' + area.acronym + '-ads' in acontent)
+            self.assertTrue('xfilter-' + area.acronym + '-chairs' in acontent)
+            self.assertTrue('xfilter-' + mars.acronym + '-ads' in acontent)
+            self.assertTrue('xfilter-' + mars.acronym + '-chairs' in acontent)
+            self.assertTrue('xfilter-' + ames.acronym + '-ads' in acontent)
+            self.assertTrue('xfilter-' + ames.acronym + '-chairs' in acontent)
+            self.assertTrue(all([x in acontent for x in [
+                'xfilter-' + area.acronym + '-ads',
+                'xfilter-' + area.acronym + '-chairs',
+                'xfilter-' + mars.acronym + '-ads',
+                'xfilter-' + mars.acronym + '-chairs',
+                'xfilter-' + ames.acronym + '-ads',
+                'xfilter-' + ames.acronym + '-chairs',
+                'xfilter-' + recent.acronym + '-ads',
+                'xfilter-' + recent.acronym + '-chairs',
+            ]]))
+            self.assertFalse(all([x in acontent for x in [
+                'xfilter-' + done.acronym + '-ads',
+                'xfilter-' + done.acronym + '-chairs',
+                'xfilter-' + wayold.acronym + '-ads',
+                'xfilter-' + wayold.acronym + '-chairs',
+            ]]))
+
+        with open(settings.GROUP_VIRTUAL_PATH) as vfile:
+            vcontent = vfile.read()
+            self.assertTrue(all([x in vcontent for x in [
+                ad.email_address(),
+                marschair.email_address(),
+                marssecr.email_address(),
+                ameschair.email_address(),
+                recentchair.email_address(),
+            ]]))
+            self.assertFalse(all([x in vcontent for x in [
+                done_ad.email_address(),
+                wayoldchair.email_address(),
+                individual.email_address(),
+            ]]))
+            self.assertTrue(all([x in vcontent for x in [
+                'xfilter-' + area.acronym + '-ads',
+                'xfilter-' + area.acronym + '-chairs',
+                'xfilter-' + mars.acronym + '-ads',
+                'xfilter-' + mars.acronym + '-chairs',
+                'xfilter-' + ames.acronym + '-ads',
+                'xfilter-' + ames.acronym + '-chairs',
+                'xfilter-' + recent.acronym + '-ads',
+                'xfilter-' + recent.acronym + '-chairs',
+            ]]))
+            self.assertFalse(all([x in vcontent for x in [
+                'xfilter-' + done.acronym + '-ads',
+                'xfilter-' + done.acronym + '-chairs',
+                'xfilter-' + wayold.acronym + '-ads',
+                'xfilter-' + wayold.acronym + '-chairs',
+            ]]))
+
+
 class GroupRoleEmailTests(TestCase):
     
     def setUp(self):
@@ -152,4 +262,3 @@ class GroupRoleEmailTests(TestCase):
             self.assertGreater(len(emails), 0)
             for item in emails:
                 self.assertIn('@', item)
-