Repository: getmoto/moto

Issue:
Cognito admin_create_user resend flow broken
PR #2357 broke the resend flow in admin_create_user.
From APIReference:

>  MessageAction
>     Set to "RESEND" to resend the invitation message to a user that already exists and reset the expiration limit on the user's account. Set to "SUPPRESS" to suppress sending the message. Only one value can be specified.
> 
>     Type: String
>     Valid Values: RESEND | SUPPRESS
>     Required: No

https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html

As a solution If the MessageAction iis given the reset value, the user should exist in the system and if not UserNotFoundException must be raised to match AWS behaviour


Discussion:

Resolving patch:
diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py
--- a/moto/cognitoidp/models.py
+++ b/moto/cognitoidp/models.py
@@ -564,12 +564,17 @@ def admin_remove_user_from_group(self, user_pool_id, group_name, username):
         user.groups.discard(group)
 
     # User
-    def admin_create_user(self, user_pool_id, username, temporary_password, attributes):
+    def admin_create_user(
+        self, user_pool_id, username, message_action, temporary_password, attributes
+    ):
         user_pool = self.user_pools.get(user_pool_id)
         if not user_pool:
             raise ResourceNotFoundError(user_pool_id)
 
-        if username in user_pool.users:
+        if message_action and message_action == "RESEND":
+            if username not in user_pool.users:
+                raise UserNotFoundError(username)
+        elif username in user_pool.users:
             raise UsernameExistsException(username)
 
         user = CognitoIdpUser(
diff --git a/moto/cognitoidp/responses.py b/moto/cognitoidp/responses.py
--- a/moto/cognitoidp/responses.py
+++ b/moto/cognitoidp/responses.py
@@ -259,10 +259,12 @@ def admin_remove_user_from_group(self):
     def admin_create_user(self):
         user_pool_id = self._get_param("UserPoolId")
         username = self._get_param("Username")
+        message_action = self._get_param("MessageAction")
         temporary_password = self._get_param("TemporaryPassword")
         user = cognitoidp_backends[self.region].admin_create_user(
             user_pool_id,
             username,
+            message_action,
             temporary_password,
             self._get_param("UserAttributes", []),
         )


Test patch:
diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py
--- a/tests/test_cognitoidp/test_cognitoidp.py
+++ b/tests/test_cognitoidp/test_cognitoidp.py
@@ -911,6 +911,55 @@ def test_admin_create_existing_user():
     caught.should.be.true
 
 
+@mock_cognitoidp
+def test_admin_resend_invitation_existing_user():
+    conn = boto3.client("cognito-idp", "us-west-2")
+
+    username = str(uuid.uuid4())
+    value = str(uuid.uuid4())
+    user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
+    conn.admin_create_user(
+        UserPoolId=user_pool_id,
+        Username=username,
+        UserAttributes=[{"Name": "thing", "Value": value}],
+    )
+
+    caught = False
+    try:
+        conn.admin_create_user(
+            UserPoolId=user_pool_id,
+            Username=username,
+            UserAttributes=[{"Name": "thing", "Value": value}],
+            MessageAction="RESEND",
+        )
+    except conn.exceptions.UsernameExistsException:
+        caught = True
+
+    caught.should.be.false
+
+
+@mock_cognitoidp
+def test_admin_resend_invitation_missing_user():
+    conn = boto3.client("cognito-idp", "us-west-2")
+
+    username = str(uuid.uuid4())
+    value = str(uuid.uuid4())
+    user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
+
+    caught = False
+    try:
+        conn.admin_create_user(
+            UserPoolId=user_pool_id,
+            Username=username,
+            UserAttributes=[{"Name": "thing", "Value": value}],
+            MessageAction="RESEND",
+        )
+    except conn.exceptions.UserNotFoundException:
+        caught = True
+
+    caught.should.be.true
+
+
 @mock_cognitoidp
 def test_admin_get_user():
     conn = boto3.client("cognito-idp", "us-west-2")