diff --git a/ex401/ex401.1.1/Dockerfile b/ex401/ex401.1.1/Dockerfile
index c2c9c95..f11899a 100644
--- a/ex401/ex401.1.1/Dockerfile
+++ b/ex401/ex401.1.1/Dockerfile
@@ -10,6 +10,7 @@ ENV USERTOKEN=ex401.1.1
 
 COPY container_files/seed-data/ /seed-data/
 COPY container_files/grouper-loader.properties /opt/grouper/conf/
+COPY container_files/grouper.client.properties /opt/grouper/conf/
 COPY container_files/subject.properties /opt/grouper/conf/
 
 RUN (/usr/sbin/slapd -h "ldap:/// ldaps:/// ldapi:///" -u ldap &) \
diff --git a/ex401/ex401.1.1/container_files/grouper-loader.properties b/ex401/ex401.1.1/container_files/grouper-loader.properties
index ae41ed1..c2e6f72 100644
--- a/ex401/ex401.1.1/container_files/grouper-loader.properties
+++ b/ex401/ex401.1.1/container_files/grouper-loader.properties
@@ -71,3 +71,22 @@ ldap.demo.tls = false
 
 #make the paths fully qualified and not relative to the loader group.
 loader.ldap.requireTopStemAsStemFromConfigGroup=false
+
+
+#####################################
+## Messaging integration with change log
+#####################################
+changeLog.consumer.rabbitMqMessagingSample.quartzCron = 0 * * * * ?                                                          
+
+# note, change "messagingSample" in key to be the name of the consumer.  e.g. changeLog.consumer.someNameAnyName.class
+changeLog.consumer.rabbitMqMessagingSample.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbConsumer
+
+changeLog.consumer.rabbitMqMessagingSample.publisher.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbMessagingPublisher
+changeLog.consumer.rabbitMqMessagingSample.publisher.messagingSystemName = rabbitmq
+# note, routingKey property is valid only for rabbitmq. For other messaging systems, it is ignored.
+changeLog.consumer.rabbitMqMessagingSample.publisher.routingKey = 
+## queue or topic
+changeLog.consumer.rabbitMqMessagingSample.publisher.messageQueueType = queue
+changeLog.consumer.rabbitMqMessagingSample.publisher.queueOrTopicName = grouper
+## this is optional if not using "id" for subjectId, need to be a subject attribute in the sources.xml
+#changeLog.consumer.rabbitMqMessagingSample.publisher.addSubjectAttributes = email
diff --git a/ex401/ex401.3.4/container_files/grouper.client.properties b/ex401/ex401.1.1/container_files/grouper.client.properties
similarity index 100%
rename from ex401/ex401.3.4/container_files/grouper.client.properties
rename to ex401/ex401.1.1/container_files/grouper.client.properties
diff --git a/ex401/ex401.1.3/container_files/seed-data/bootstrap.gsh b/ex401/ex401.1.3/container_files/seed-data/bootstrap.gsh
index 8324428..f6cd64b 100644
--- a/ex401/ex401.1.3/container_files/seed-data/bootstrap.gsh
+++ b/ex401/ex401.1.3/container_files/seed-data/bootstrap.gsh
@@ -5,7 +5,11 @@ addStem("app:vpn", "ref", "ref");
 
 addGroup("app:vpn:ref", "vpn_adhoc", "vpn_adhoc");
 addGroup("app:vpn", "vpn_authorized", "vpn_authorized");
+addGroup("app:vpn", "vpn_allow", "vpn_allow");
+addGroup("app:vpn", "vpn_deny", "vpn_deny");
 
-addMember("app:vpn:vpn_authorized", "ref:faculty");
-addMember("app:vpn:vpn_authorized", "ref:staff");
-addMember("app:vpn:vpn_authorized", "app:vpn:ref:vpn_adhoc");
+addMember("app:vpn:vpn_allow", "ref:faculty");
+addMember("app:vpn:vpn_allow", "ref:staff");
+addMember("app:vpn:vpn_allow", "app:vpn:ref:vpn_adhoc");
+
+addComposite("app:vpn:vpn_authorized", CompositeType.COMPLEMENT, "app:vpn:vpn_allow", "app:vpn:vpn_deny");
diff --git a/ex401/ex401.1.4/container_files/grouper-loader.properties b/ex401/ex401.1.4/container_files/grouper-loader.properties
index ddcb809..ffab1fc 100644
--- a/ex401/ex401.1.4/container_files/grouper-loader.properties
+++ b/ex401/ex401.1.4/container_files/grouper-loader.properties
@@ -87,3 +87,21 @@ changeLog.consumer.pspng_groupOfNames.groupCreationLdifTemplate = dn: cn=${group
 changeLog.consumer.pspng_groupOfNames.userSearchBaseDn = ou=people,dc=internet2,dc=edu
 changeLog.consumer.pspng_groupOfNames.userSearchFilter = uid=${subject.id}
 changeLog.consumer.pspng_groupOfNames.grouperIsAuthoritative = false
+
+#####################################
+## Messaging integration with change log
+#####################################
+changeLog.consumer.rabbitMqMessagingSample.quartzCron = 0 * * * * ?                                                          
+
+# note, change "messagingSample" in key to be the name of the consumer.  e.g. changeLog.consumer.someNameAnyName.class
+changeLog.consumer.rabbitMqMessagingSample.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbConsumer
+
+changeLog.consumer.rabbitMqMessagingSample.publisher.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbMessagingPublisher
+changeLog.consumer.rabbitMqMessagingSample.publisher.messagingSystemName = rabbitmq
+# note, routingKey property is valid only for rabbitmq. For other messaging systems, it is ignored.
+changeLog.consumer.rabbitMqMessagingSample.publisher.routingKey = 
+## queue or topic
+changeLog.consumer.rabbitMqMessagingSample.publisher.messageQueueType = queue
+changeLog.consumer.rabbitMqMessagingSample.publisher.queueOrTopicName = grouper
+## this is optional if not using "id" for subjectId, need to be a subject attribute in the sources.xml
+#changeLog.consumer.rabbitMqMessagingSample.publisher.addSubjectAttributes = email
diff --git a/ex401/ex401.1.5/container_files/seed-data/bootstrap.gsh b/ex401/ex401.1.5/container_files/seed-data/bootstrap.gsh
index 5942048..641c44e 100644
--- a/ex401/ex401.1.5/container_files/seed-data/bootstrap.gsh
+++ b/ex401/ex401.1.5/container_files/seed-data/bootstrap.gsh
@@ -3,9 +3,7 @@ gs = GrouperSession.startRootSession();
 addStem("ref", "iam", "iam");
 addGroup("ref:iam", "global_deny", "global_deny");
 
-addGroup("app:vpn", "vpn_allow", "vpn_allow");
-addGroup("app:vpn", "vpn_deny", "vpn_deny");
-addMember("app:vpn:vpn_deny", "ref:iam:gobal_deny");
+addMember("app:vpn:vpn_deny", "ref:iam:global_deny");
 
 group=addGroup("app:vpn:ref", "vpn_ajohnson409", "vpn_ajohnson409");
 group.setDescription("special project managed by ajohnson409");
@@ -20,14 +18,6 @@ group=addGroup("app:vpn:ref", "vpn_consultants", "vpn_consultants");
 group.setDescription("Consultants, must be approved by VP and have expiration date set");
 group.store();
 
-//Refactoring group membership
-delGroup("app:vpn:vpn_authorized");
-addGroup("app:vpn", "vpn_authorized", "vpn_authorized");
-addComposite("app:vpn:vpn_authorized", CompositeType.COMPLEMENT, "app:vpn:vpn_allow", "app:vpn:vpn_deny");
-
-//Assign the PSPNG attribute for the standard groups (needs to match 401.1.4's initial settings)
-group = GroupFinder.findByName(gs, "app:vpn:vpn_authorized");
-
 # Auto create the PSPNG attributes
 # edu.internet2.middleware.grouper.pspng.FullSyncProvisionerFactory.getFullSyncer("pspng_groupOfNames");
 
@@ -39,10 +29,6 @@ attributeAssignSave.addValue("pspng_groupOfNames");
 attributeAssignSave.save();
 
 
-addMember("app:vpn:vpn_allow", "ref:faculty");
-addMember("app:vpn:vpn_allow", "ref:staff");
-addMember("app:vpn:vpn_allow", "ref:student");
-addMember("app:vpn:vpn_allow", "app:vpn:ref:vpn_adhoc");
 addMember("app:vpn:ref:vpn_adhoc", "app:vpn:ref:vpn_ajohnson409");
 addMember("app:vpn:ref:vpn_adhoc", "app:vpn:ref:vpn_consultants");
 
@@ -72,7 +58,7 @@ attributeAssignSave.addAttributeAssignOnThisAssignment(attributeAssignOnAssignSa
 attributeAssign = attributeAssignSave.save();
 
 
-# Groovy Script - Auto set expiration date on membership:
+// Groovy Script - Auto set expiration date on membership:
 numDays = 32;
 actAs = SubjectFinder.findRootSubject();
 vpn_adhoc = getGroups("app:vpn:ref:vpn_adhoc")[0];
diff --git a/ex401/ex401.2.3/container_files/grouper-loader.properties b/ex401/ex401.2.3/container_files/grouper-loader.properties
index 1050e7f..5a38a2a 100644
--- a/ex401/ex401.2.3/container_files/grouper-loader.properties
+++ b/ex401/ex401.2.3/container_files/grouper-loader.properties
@@ -98,3 +98,21 @@ changeLog.consumer.pspng_entitlements.provisionedAttributeValueFormat = ${group.
 changeLog.consumer.pspng_entitlements.userSearchBaseDn = ou=people,dc=internet2,dc=edu
 changeLog.consumer.pspng_entitlements.userSearchFilter = uid=${subject.id}
 changeLog.consumer.pspng_entitlements.allProvisionedValuesPrefix=*
+
+#####################################
+## Messaging integration with change log
+#####################################
+changeLog.consumer.rabbitMqMessagingSample.quartzCron = 0 * * * * ?                                                          
+
+# note, change "messagingSample" in key to be the name of the consumer.  e.g. changeLog.consumer.someNameAnyName.class
+changeLog.consumer.rabbitMqMessagingSample.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbConsumer
+
+changeLog.consumer.rabbitMqMessagingSample.publisher.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbMessagingPublisher
+changeLog.consumer.rabbitMqMessagingSample.publisher.messagingSystemName = rabbitmq
+# note, routingKey property is valid only for rabbitmq. For other messaging systems, it is ignored.
+changeLog.consumer.rabbitMqMessagingSample.publisher.routingKey = 
+## queue or topic
+changeLog.consumer.rabbitMqMessagingSample.publisher.messageQueueType = queue
+changeLog.consumer.rabbitMqMessagingSample.publisher.queueOrTopicName = grouper
+## this is optional if not using "id" for subjectId, need to be a subject attribute in the sources.xml
+#changeLog.consumer.rabbitMqMessagingSample.publisher.addSubjectAttributes = email
diff --git a/ex401/ex401.2.8/container_files/seed-data/bootstrap.gsh b/ex401/ex401.2.8/container_files/seed-data/bootstrap.gsh
index bc439e5..a05060e 100644
--- a/ex401/ex401.2.8/container_files/seed-data/bootstrap.gsh
+++ b/ex401/ex401.2.8/container_files/seed-data/bootstrap.gsh
@@ -4,17 +4,18 @@ addGroup("app:mfa", "mfa_required", "mfa_required");
 addGroup("app:mfa:ref", "mfa_opt_in", "mfa_opt_in");
 addMember("app:mfa:mfa_enabled_allow", "app:mfa:ref:mfa_opt_in");
 
-addGroup("app:mfa:ref", "mfa_opt_in_access", "mfa_opt_in_access");
-addGroup("app:mfa:ref", "mfa_opt_in_access_allow", "mfa_opt_in_access_allow");
-addGroup("app:mfa:ref", "mfa_opt_in_access_deny", "mfa_opt_in_access_deny");
+addStem("app:mfa", "etc", "etc")
+addGroup("app:mfa:etc", "mfa_opt_in_access", "mfa_opt_in_access");
+addGroup("app:mfa:etc", "mfa_opt_in_access_allow", "mfa_opt_in_access_allow");
+addGroup("app:mfa:etc", "mfa_opt_in_access_deny", "mfa_opt_in_access_deny");
 
-addComposite("app:mfa:ref:mfa_opt_in_access", CompositeType.COMPLEMENT, "app:mfa:ref:mfa_opt_in_access_allow", "app:mfa:ref:mfa_opt_in_access_deny");
+addComposite("app:mfa:etc:mfa_opt_in_access", CompositeType.COMPLEMENT, "app:mfa:etc:mfa_opt_in_access_allow", "app:mfa:etc:mfa_opt_in_access_deny");
 
-addMember("app:mfa:ref:mfa_opt_in_access_allow", "ref:faculty");
-addMember("app:mfa:ref:mfa_opt_in_access_allow", "ref:staff");
-addMember("app:mfa:ref:mfa_opt_in_access_allow", "ref:student");
+addMember("app:mfa:etc:mfa_opt_in_access_allow", "ref:faculty");
+addMember("app:mfa:etc:mfa_opt_in_access_allow", "ref:staff");
+addMember("app:mfa:etc:mfa_opt_in_access_allow", "ref:student");
 
-addMember("app:mfa:ref:mfa_opt_in_access_deny", "app:mfa:mfa_required");
+addMember("app:mfa:etc:mfa_opt_in_access_deny", "app:mfa:mfa_required");
 
-grantPriv("app:mfa:ref:mfa_opt_in", "app:mfa:ref:mfa_opt_in_access", AccessPrivilege.OPTIN);
-grantPriv("app:mfa:ref:mfa_opt_in", "app:mfa:ref:mfa_opt_in_access", AccessPrivilege.OPTOUT);
+grantPriv("app:mfa:ref:mfa_opt_in", "app:mfa:etc:mfa_opt_in_access", AccessPrivilege.OPTIN);
+grantPriv("app:mfa:ref:mfa_opt_in", "app:mfa:etc:mfa_opt_in_access", AccessPrivilege.OPTOUT);
diff --git a/ex401/ex401.2.9/container_files/seed-data/bootstrap.gsh b/ex401/ex401.2.9/container_files/seed-data/bootstrap.gsh
index 0e2ad1a..dabd890 100644
--- a/ex401/ex401.2.9/container_files/seed-data/bootstrap.gsh
+++ b/ex401/ex401.2.9/container_files/seed-data/bootstrap.gsh
@@ -6,7 +6,7 @@ group = GroupFinder.findByName(gs, "app:mfa:ref:mfa_bypass", true);
 stem = StemFinder.findByName(gs, "app:mfa:basis", true);
 group.move(stem);
 
-addGroup("app:mfa:ref", "bypass-not-opt-in", "bypass-not-opt-in");
-addComposite("app:mfa:ref:bypass-not-opt-in", CompositeType.COMPLEMENT, "app:mfa:basis:mfa_bypass", "app:mfa:ref:mfa_opt_in");
+addGroup("app:mfa:ref", "mfa_bypass_not_opt_in", "mfa_bypass_not_opt_in");
+addComposite("app:mfa:ref:mfa_bypass_not_opt_in", CompositeType.COMPLEMENT, "app:mfa:basis:mfa_bypass", "app:mfa:ref:mfa_opt_in");
 
-addMember("app:mfa:mfa_enabled_deny", "app:mfa:ref:bypass-not-opt-in");
+addMember("app:mfa:mfa_enabled_deny", "app:mfa:ref:mfa_bypass_not_opt_in");
diff --git a/ex401/ex401.2.end/container_files/seed-data/bootstrap.gsh b/ex401/ex401.2.end/container_files/seed-data/bootstrap.gsh
index 1289355..875e82f 100644
--- a/ex401/ex401.2.end/container_files/seed-data/bootstrap.gsh
+++ b/ex401/ex401.2.end/container_files/seed-data/bootstrap.gsh
@@ -3,13 +3,15 @@ gs = GrouperSession.startRootSession();
 addMember("app:mfa:mfa_enabled_allow", "ref:faculty");
 addMember("app:mfa:mfa_enabled_allow", "ref:staff");
 addMember("app:mfa:mfa_enabled_allow", "ref:student");
+delMember("app:mfa:mfa_enabled_allow", "ref:dept:Information Technology");
 
 delGroup("app:mfa:ref:pilot");
-delGroup("app:mfa:ref:mfa_opt_in_access");
-delGroup("app:mfa:ref:mfa_opt_in_access_allow");
-delGroup("app:mfa:ref:mfa_opt_in_access_deny");
+delGroup("app:mfa:etc:mfa_opt_in_access");
+delGroup("app:mfa:etc:mfa_opt_in_access_allow");
+delGroup("app:mfa:etc:mfa_opt_in_access_deny");
 delGroup("app:mfa:ref:mfa_opt_in");
-delGroup("app:mfa:ref:bypass-not-opt-in");
+delGroup("app:mfa:ref:mfa_bypass_not_opt_in");
+delGroup("app:mfa:mfa_required");
 delGroup("app:mfa:ref:BannerUsersMinusFaculty");
 delGroup("app:mfa:ref:NonFacultyBannerINB");
-delGroup("app:mfa:ref:athletics_dept");
\ No newline at end of file
+delGroup("app:mfa:ref:athletics_dept");
diff --git a/ex401/ex401.3.4/Dockerfile b/ex401/ex401.3.4/Dockerfile
index 1a409e4..ef48d09 100644
--- a/ex401/ex401.3.4/Dockerfile
+++ b/ex401/ex401.3.4/Dockerfile
@@ -9,8 +9,6 @@ LABEL author="tier-packaging@internet2.edu <tier-packaging@internet2.edu>" \
 ENV USERTOKEN=ex401.3.4
 
 COPY container_files/seed-data/ /seed-data/
-COPY container_files/grouper-loader.properties /opt/grouper/conf/
-COPY container_files/grouper.client.properties /opt/grouper/conf/
 COPY container_files/attribute-filter.xml /opt/shibboleth-idp/conf/
 
 RUN . /usr/local/bin/library.sh \
diff --git a/ex401/ex401.3.4/container_files/grouper-loader.properties b/ex401/ex401.3.4/container_files/grouper-loader.properties
deleted file mode 100644
index 45f2b18..0000000
--- a/ex401/ex401.3.4/container_files/grouper-loader.properties
+++ /dev/null
@@ -1,118 +0,0 @@
-#specify the consumers here.  specify the consumer name after the changeLog.consumer. part.  This example is "psp"
-#but it could be changeLog.consumer.myConsumerName.class
-#the class must extend edu.internet2.middleware.grouper.changeLog.ChangeLogConsumerBase
-#changeLog.consumer.psp.class = edu.internet2.middleware.psp.grouper.PspChangeLogConsumer
-
-#the quartz cron is a cron-like string.  it defaults to every minute on the minute (since the temp to change log job runs
-#at 10 seconds to each minute).  it defaults to this: 0 * * * * ?                                          
-#though it will stagger each one by 2 seconds                                                              
-# http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger                           
-#changeLog.consumer.psp.quartzCron = 0 * * * * ?                                                          
-                                                                                                           
-# To retry processing a change log entry if an error occurs, set retryOnError to true. Defaults to false.  
-#changeLog.consumer.psp.retryOnError = false                                                              
-                                                                                                           
-# To run full provisioning synchronizations periodically, provide the class name which provides a 'public void fullSync()' method.
-#changeLog.psp.fullSync.class = edu.internet2.middleware.psp.grouper.PspChangeLogConsumer                 
-                                                                                                           
-# Schedule full synchronizations. Defaults to 5 am : 0 0 5 * * ?.                                          
-#changeLog.psp.fullSync.quartzCron = 0 0 5 * * ?
-                                                                                                           
-# Run a full synchronization job at startup. Defaults to false.                                            
-#changeLog.psp.fullSync.runAtStartup = false                                                              
-                                                                                                           
-# Omit diff responses from bulk response to conserve memory.                                               
-#changeLog.psp.fullSync.omitDiffResponses = true                                                          
-                                                                                                           
-# Omit sync responses from bulk response to conserve memory.                                               
-#changeLog.psp.fullSync.omitSyncResponses = true 
-
-#################################
-## LDAP connections
-#################################
-# specify the ldap connection with user, pass, url
-# the string after "ldap." is the ID of the connection, and it should not have
-# spaces or other special chars in it.  In this case is it "personLdap"
- 
-#note the URL should start with ldap: or ldaps: if it is SSL.  
-#It should contain the server and port (optional if not default), and baseDn,
-#e.g. ldaps://ldapserver.school.edu:636/dc=school,dc=edu
-ldap.demo.url = ldap://localhost:389/
- 
-#optional, if authenticated
-ldap.demo.user = cn=root,dc=internet2,dc=edu
- 
-#optional, if authenticated note the password can be stored encrypted in an external file
-ldap.demo.pass = password
- 
-#optional, if you are using tls, set this to true.  Generally you will not be using an SSL URL to use TLS...
-ldap.demo.tls = false
- 
-#optional, if using sasl
-#ldap.personLdap.saslAuthorizationId =
-#ldap.personLdap.saslRealm =
- 
-#optional (note, time limit is for search operations, timeout is for connection timeouts),
-#most of these default to vt-ldap defaults.  times are in millis
-#validateOnCheckout defaults to true if all other validate methods are false
-#ldap.personLdap.batchSize =
-#ldap.personLdap.countLimit =
-#ldap.personLdap.timeLimit =
-#ldap.personLdap.timeout =
-#ldap.personLdap.minPoolSize =
-#ldap.personLdap.maxPoolSize =
-#ldap.personLdap.validateOnCheckIn =
-#ldap.personLdap.validateOnCheckOut =
-#ldap.personLdap.validatePeriodically =
-#ldap.personLdap.validateTimerPeriod =
-#ldap.personLdap.pruneTimerPeriod =
-#if connections expire after a certain amount of time, this is it, in millis, defaults to 300000 (5 minutes)
-#ldap.personLdap.expirationTime =
-
-#make the paths fully qualified and not relative to the loader group.
-loader.ldap.requireTopStemAsStemFromConfigGroup=false
-
-changeLog.consumer.pspng_groupOfNames.class = edu.internet2.middleware.grouper.pspng.PspChangelogConsumerShim
-changeLog.consumer.pspng_groupOfNames.type = edu.internet2.middleware.grouper.pspng.LdapGroupProvisioner
-changeLog.consumer.pspng_groupOfNames.quartzCron = 0 * * * * ?
-changeLog.consumer.pspng_groupOfNames.ldapPoolName = demo
-changeLog.consumer.pspng_groupOfNames.supportsEmptyGroups = false
-changeLog.consumer.pspng_groupOfNames.memberAttributeName = member
-changeLog.consumer.pspng_groupOfNames.memberAttributeValueFormat = ${ldapUser.getDn()}
-changeLog.consumer.pspng_groupOfNames.groupSearchBaseDn = ou=groups,dc=internet2,dc=edu
-changeLog.consumer.pspng_groupOfNames.allGroupsSearchFilter = objectclass=groupOfNames
-changeLog.consumer.pspng_groupOfNames.singleGroupSearchFilter = (&(objectclass=groupOfNames)(cn=${group.name}))
-changeLog.consumer.pspng_groupOfNames.groupSearchAttributes = cn,objectclass
-changeLog.consumer.pspng_groupOfNames.groupCreationLdifTemplate = dn: cn=${group.name}||cn: ${group.name}||objectclass: groupOfNames
-changeLog.consumer.pspng_groupOfNames.userSearchBaseDn = ou=people,dc=internet2,dc=edu
-changeLog.consumer.pspng_groupOfNames.userSearchFilter = uid=${subject.id}
-changeLog.consumer.pspng_groupOfNames.grouperIsAuthoritative = false
-
-
-changeLog.consumer.pspng_entitlements.class = edu.internet2.middleware.grouper.pspng.PspChangelogConsumerShim
-changeLog.consumer.pspng_entitlements.type = edu.internet2.middleware.grouper.pspng.LdapAttributeProvisioner
-changeLog.consumer.pspng_entitlements.quartzCron = 0 * * * * ?
-changeLog.consumer.pspng_entitlements.ldapPoolName = demo
-changeLog.consumer.pspng_entitlements.provisionedAttributeName = eduPersonEntitlement
-changeLog.consumer.pspng_entitlements.provisionedAttributeValueFormat = ${group.name.equalsIgnoreCase('app:mfa:mfa_enabled') ? 'http://tier.internet2.edu/mfa/enabled' : (group.name.equalsIgnoreCase('app:boardeffect:boardeffect_authorized') ? 'https://college.boardeffect.com/' : 'urn:mace:example.edu:' + group.extension) }
-changeLog.consumer.pspng_entitlements.userSearchBaseDn = ou=people,dc=internet2,dc=edu
-changeLog.consumer.pspng_entitlements.userSearchFilter = uid=${subject.id}
-changeLog.consumer.pspng_entitlements.allProvisionedValuesPrefix=*
-
-#####################################
-## Messaging integration with change log
-#####################################
-changeLog.consumer.rabbitMqMessagingSample.quartzCron = 0 * * * * ?                                                          
-
-# note, change "messagingSample" in key to be the name of the consumer.  e.g. changeLog.consumer.someNameAnyName.class
-changeLog.consumer.rabbitMqMessagingSample.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbConsumer
-
-changeLog.consumer.rabbitMqMessagingSample.publisher.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbMessagingPublisher
-changeLog.consumer.rabbitMqMessagingSample.publisher.messagingSystemName = rabbitmq
-# note, routingKey property is valid only for rabbitmq. For other messaging systems, it is ignored.
-changeLog.consumer.rabbitMqMessagingSample.publisher.routingKey = 
-## queue or topic
-changeLog.consumer.rabbitMqMessagingSample.publisher.messageQueueType = queue
-changeLog.consumer.rabbitMqMessagingSample.publisher.queueOrTopicName = grouper
-## this is optional if not using "id" for subjectId, need to be a subject attribute in the sources.xml
-#changeLog.consumer.rabbitMqMessagingSample.publisher.addSubjectAttributes = email
diff --git a/ex401/ex401.4.end/container_files/seed-data/bootstrap.gsh b/ex401/ex401.4.end/container_files/seed-data/bootstrap.gsh
index 19c8cd5..989df48 100644
--- a/ex401/ex401.4.end/container_files/seed-data/bootstrap.gsh
+++ b/ex401/ex401.4.end/container_files/seed-data/bootstrap.gsh
@@ -74,18 +74,14 @@ makeStemInheritable(this, stem.name, view_group.name, 'read');
 admin_group.revokePriv(mgr_group.toMember().getSubject(), AccessPrivilege.UPDATE);
 
 
-addGroup("ref", "community", "community");
-
 
 #addStem("app", "lms", "lms");
 group = addGroup("app:lms", "lms_authorized", "lms_authorized");
-addGroup("app:lms", "lms_allow", "lms_allow");
-addGroup("app:lms", "lms_deny", "lms_deny");
-addComposite("app:lms:lms_authorized", CompositeType.COMPLEMENT, "app:lms:lms_allow", "app:lms:lms_deny");
-
-addMember("app:lms:lms_allow", "ref:community");
-
+addGroup("app:lms", "lms_authorized_allow", "lms_authorized_allow");
+addGroup("app:lms", "lms_authorized_deny", "lms_authorized_deny");
+addComposite("app:lms:lms_authorized", CompositeType.COMPLEMENT, "app:lms:lms_authorized_allow", "app:lms:lms_authorized_deny");
 
+addMember("app:lms:lms_authorized_allow", "ref:legacy:community_members");
 
 pspngAttribute = AttributeDefNameFinder.findByName("etc:pspng:provision_to", true);
 AttributeAssignSave attributeAssignSave = new AttributeAssignSave(gs).assignPrintChangesToSystemOut(true);
@@ -95,10 +91,6 @@ attributeAssignSave.addValue("pspng_groupOfNames");
 attributeAssignSave.save();
 
 
-addGroup("app:lms", "lms_missing_community_members", "lms_missing_community_members");
-addComposite("app:lms:lms_missing_community_members", CompositeType.COMPLEMENT, "ref:legacy:community_members", "app:lms:lms_authorized");
-
-
 addStem("app:lms", "ref", "ref");
 
 
@@ -130,7 +122,7 @@ addMember("app:lms:ref:visiting_scholars","clopez383");
 addMember("app:lms:ref:visiting_scholars","apeterson387");
 
 
-addMember("app:lms:lms_allow", "app:lms:ref:visiting_scholars");
+addMember("app:lms:lms_authorized_allow", "app:lms:ref:visiting_scholars");
 
 pspngAttribute = AttributeDefNameFinder.findByName("etc:pspng:provision_to", true);
 AttributeAssignSave attributeAssignSave = new AttributeAssignSave(gs).assignPrintChangesToSystemOut(true);
diff --git a/manualBuild.sh b/manualBuild.sh
index 27b14e0..709c59e 100755
--- a/manualBuild.sh
+++ b/manualBuild.sh
@@ -1,7 +1,7 @@
 docker build --pull --tag=tier/grouper-training-env:base base/ \
 && docker build --tag=tier/grouper-training-env:full_demo full-demo \
 && docker build --tag=tier/grouper-training-env:ex201.1.1 ex201/ex201.1.1 \
-&& docker build --tag=tier/grouper-training-env:ex201.1.end ex201/ex201.1.end
+&& docker build --tag=tier/grouper-training-env:ex201.1.end ex201/ex201.1.end \
 && docker build --tag=tier/grouper-training-env:ex401.1.1 ex401/ex401.1.1 \
 && docker build --tag=tier/grouper-training-env:ex401.1.2 ex401/ex401.1.2 \
 && docker build --tag=tier/grouper-training-env:ex401.1.3 ex401/ex401.1.3 \