diff --git a/master/bt5/slapos_cloud/PathTemplateItem/portal_alarms/slapos_cloud_create_missing_subscription_assignment.xml b/master/bt5/slapos_cloud/PathTemplateItem/portal_alarms/slapos_cloud_create_missing_subscription_assignment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7acc3fa20e6a5adb9de9b2cabe59447c5ee03911
--- /dev/null
+++ b/master/bt5/slapos_cloud/PathTemplateItem/portal_alarms/slapos_cloud_create_missing_subscription_assignment.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Alarm" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>active_sense_method_id</string> </key>
+            <value> <string>Alarm_createMissingSubscriptionAssignment</string> </value>
+        </item>
+        <item>
+            <key> <string>automatic_solve</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>enabled</string> </key>
+            <value> <int>1</int> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>slapos_cloud_create_missing_subscription_assignment</string> </value>
+        </item>
+        <item>
+            <key> <string>periodicity_day_frequency</string> </key>
+            <value> <int>1</int> </value>
+        </item>
+        <item>
+            <key> <string>periodicity_hour</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>periodicity_minute</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>periodicity_month</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>periodicity_month_day</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>periodicity_week</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string>Create Missing Subscription Assignment</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.py b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c0d70363a86b7adcdf347e1635a079690bc6aae
--- /dev/null
+++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.py
@@ -0,0 +1,27 @@
+portal = context.getPortalObject()
+
+assignment_category_list = portal.portal_preferences.getPreferredSubscriptionAssignmentCategoryList()
+is_default_project_defined = False
+for assignment_category in assignment_category_list:
+  if assignment_category.startswith('destination_project/project_module/'):
+    is_default_project_defined = True
+
+if not is_default_project_defined:
+  return
+
+portal.portal_catalog.searchAndActivate(
+  portal_type='Assignment',
+  # Do not create assignment if the Person does not have at least one
+  validation_state='open',
+  # Prevent checking the same person multiple times
+  group_by=['parent_uid'],
+  method_id='Assignment_createPersonMissingSubscriptionAssignment',
+  # This alarm bruteforce checking all documents,
+  # without changing them directly.
+  # Increase priority to not block other activities
+  method_args=[assignment_category_list],
+  method_kw={"activate_kw": {'tag': tag, 'priority': 2}},
+  activate_kw={'tag': tag, 'priority': 2}
+)
+
+context.activate(after_tag=tag).getId()
diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.xml b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cb739ef3e4383adc61275b1a03d0727bdcb5c4cd
--- /dev/null
+++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_bind_names</string> </key>
+            <value>
+              <object>
+                <klass>
+                  <global name="_reconstructor" module="copy_reg"/>
+                </klass>
+                <tuple>
+                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
+                  <global name="object" module="__builtin__"/>
+                  <none/>
+                </tuple>
+                <state>
+                  <dictionary>
+                    <item>
+                        <key> <string>_asgns</string> </key>
+                        <value>
+                          <dictionary>
+                            <item>
+                                <key> <string>name_container</string> </key>
+                                <value> <string>container</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_context</string> </key>
+                                <value> <string>context</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_m_self</string> </key>
+                                <value> <string>script</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_subpath</string> </key>
+                                <value> <string>traverse_subpath</string> </value>
+                            </item>
+                          </dictionary>
+                        </value>
+                    </item>
+                  </dictionary>
+                </state>
+              </object>
+            </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>tag, fixit, params</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>Alarm_createMissingSubscriptionAssignment</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.py b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.py
new file mode 100644
index 0000000000000000000000000000000000000000..549163e409903aa2e1dc755615c2ae33937b7188
--- /dev/null
+++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.py
@@ -0,0 +1,26 @@
+from zExceptions import Unauthorized
+if REQUEST is not None:
+  raise Unauthorized
+
+assignment_category_set = set(assignment_category_list)
+person = context.getParentValue()
+
+# check if user has an assignment matching the default preference
+has_default_assignment = False
+has_open_assignment = False
+for assignment in person.contentValues(
+  portal_type='Assignment'
+):
+  if assignment.getValidationState() != 'open':
+    continue
+  has_open_assignment = True
+  has_default_assignment = has_default_assignment or (assignment_category_set <= set(assignment.getCategoryList()))
+
+if has_open_assignment and (not has_default_assignment):
+  new_assignment = person.newContent(
+    portal_type="Assignment",
+    title="Preferred Subscription Assignment"
+  )
+  new_assignment.setCategoryList(assignment_category_list)
+  new_assignment.open(comment="Created during the system preference subscription definition")
+  new_assignment.reindexObject(activate_kw=activate_kw)
diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.xml b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ea845d287d4b4b52dfb5e66f3e9ceb4322a791e0
--- /dev/null
+++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_bind_names</string> </key>
+            <value>
+              <object>
+                <klass>
+                  <global name="_reconstructor" module="copy_reg"/>
+                </klass>
+                <tuple>
+                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
+                  <global name="object" module="__builtin__"/>
+                  <none/>
+                </tuple>
+                <state>
+                  <dictionary>
+                    <item>
+                        <key> <string>_asgns</string> </key>
+                        <value>
+                          <dictionary>
+                            <item>
+                                <key> <string>name_container</string> </key>
+                                <value> <string>container</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_context</string> </key>
+                                <value> <string>context</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_m_self</string> </key>
+                                <value> <string>script</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_subpath</string> </key>
+                                <value> <string>traverse_subpath</string> </value>
+                            </item>
+                          </dictionary>
+                        </value>
+                    </item>
+                  </dictionary>
+                </state>
+              </object>
+            </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>assignment_category_list, activate_kw=None, REQUEST=None</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>Assignment_createPersonMissingSubscriptionAssignment</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py
index 6857f2077236ffa8b6997c9de61232592ffc7e9d..8803699291e613cf17d9d2337205b56042407089 100644
--- a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py
+++ b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py
@@ -7,6 +7,134 @@ from erp5.component.module.DateUtils import addToDate
 
 
 class TestSlapOSCoreSlapOSAssertInstanceTreeSuccessorAlarm(SlapOSTestCaseMixin):
+  #################################################################
+  # slapos_cloud_create_missing_subscription_assignment
+  #################################################################
+  def test_Assignment_createPersonMissingSubscriptionAssignment_alarm_notOpenAssignment(self):
+    # some preparation
+    project = self.portal.project_module.newContent()
+    preference = self.portal.portal_preferences.slapos_default_system_preference
+    preference.edit(
+      preferred_subscription_assignment_category_list=[
+        'destination_project/%s' % project.getRelativeUrl()
+      ]
+    )
+
+    person = self.portal.person_module.newContent(
+      portal_type='Person'
+    )
+    assignment = person.newContent(
+      portal_type='Assignment'
+    )
+    self.tic()
+    self._test_alarm_not_visited(
+      self.portal.portal_alarms.slapos_cloud_create_missing_subscription_assignment,
+      assignment,
+      'Assignment_createPersonMissingSubscriptionAssignment'
+    )
+
+  def test_Assignment_createPersonMissingSubscriptionAssignment_alarm_oneAssignment(self):
+    # some preparation
+    project = self.portal.project_module.newContent()
+    preference = self.portal.portal_preferences.slapos_default_system_preference
+    preference.edit(
+      preferred_subscription_assignment_category_list=[
+        'destination_project/%s' % project.getRelativeUrl()
+      ]
+    )
+
+    person = self.portal.person_module.newContent(
+      portal_type='Person'
+    )
+    assignment = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment.open()
+    self.tic()
+    self._test_alarm(
+      self.portal.portal_alarms.slapos_cloud_create_missing_subscription_assignment,
+      assignment,
+      'Assignment_createPersonMissingSubscriptionAssignment'
+    )
+
+  """
+  def test_Assignment_createPersonMissingSubscriptionAssignment_alarm_twoAssignment(self):
+    person = self.portal.person_module.newContent(
+      portal_type='Person'
+    )
+    assignment1 = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment1.open()
+    assignment2 = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment2.open()
+    self.tic()
+    self._test_alarm(
+      self.portal.portal_alarms.slapos_cloud_create_missing_subscription_assignment,
+      assignment1,
+      'Assignment_createPersonMissingSubscriptionAssignment'
+    )
+  """
+
+  #################################################################
+  # Assignment_createPersonMissingSubscriptionAssignment
+  #################################################################
+  def test_Assignment_createPersonMissingSubscriptionAssignment_script_noSubscriptionAssignment(self):
+    subscription_category_list = ['activity/research', 'role/member']
+    person = self.portal.person_module.newContent(
+      portal_type='Person'
+    )
+    assignment = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment.open()
+
+    assignment.Assignment_createPersonMissingSubscriptionAssignment(subscription_category_list)
+
+    self.assertEquals(2, len(person.contentValues(portal_type='Assignment')))
+    subscription_assignment = [x for x in person.contentValues(portal_type='Assignment') if x.getId() != assignment.getId()][0]
+    self.assertEquals('open', subscription_assignment.getValidationState())
+    self.assertSameSet(subscription_category_list, subscription_assignment.getCategoryList())
+
+  def test_Assignment_createPersonMissingSubscriptionAssignment_script_withOpenSubscriptionAssignment(self):
+    subscription_category_list = ['activity/research', 'role/member']
+    person = self.portal.person_module.newContent(
+      portal_type='Person'
+    )
+    assignment = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment.setCategoryList(subscription_category_list)
+    assignment.open()
+
+    assignment.Assignment_createPersonMissingSubscriptionAssignment(subscription_category_list)
+
+    self.assertEquals(1, len(person.contentValues(portal_type='Assignment')))
+
+  def test_Assignment_createPersonMissingSubscriptionAssignment_script_withDraftSubscriptionAssignment(self):
+    subscription_category_list = ['activity/research', 'role/member']
+    person = self.portal.person_module.newContent(
+      portal_type='Person'
+    )
+    assignment1 = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment1.setCategoryList(subscription_category_list)
+
+    assignment2 = person.newContent(
+      portal_type='Assignment'
+    )
+    assignment2.open()
+
+    assignment2.Assignment_createPersonMissingSubscriptionAssignment(subscription_category_list)
+
+    self.assertEquals(3, len(person.contentValues(portal_type='Assignment')))
+    subscription_assignment = [x for x in person.contentValues(portal_type='Assignment') if (x.getId() != assignment1.getId()) and (x.getId() != assignment2.getId())][0]
+    self.assertEquals('open', subscription_assignment.getValidationState())
+    self.assertSameSet(subscription_category_list, subscription_assignment.getCategoryList())
+
   #################################################################
   # slapos_assert_instance_tree_successor
   #################################################################
diff --git a/master/bt5/slapos_cloud/bt/template_path_list b/master/bt5/slapos_cloud/bt/template_path_list
index 3dc08867b0d95145ccff304cf49c60ca7d954011..ae6d25d25b8bf41c81909432ae9af5c743fce6c4 100644
--- a/master/bt5/slapos_cloud/bt/template_path_list
+++ b/master/bt5/slapos_cloud/bt/template_path_list
@@ -3,6 +3,7 @@ acl_users/slapos_machine
 acl_users/slapos_shadow
 portal_alarms/slapos_allocate_instance
 portal_alarms/slapos_assert_instance_tree_successor
+portal_alarms/slapos_cloud_create_missing_subscription_assignment
 portal_alarms/slapos_cloud_garbage_collect_one_time_virtual_master_access_token
 portal_alarms/slapos_cloud_invalidate_closed_compute_node
 portal_alarms/slapos_cloud_invalidate_destroyed_instance