Repository: getmoto/moto

Issue:
NotImplementedError: The list_users action has not been implemented
```
raise NotImplementedError("The {0} action has not been implemented".format(action))
NotImplementedError: The describe_stack_events action has not been implemented
```

Based on the repo, it looks like the IAM call list_users has not yet been implemented in moto.



Discussion:

Resolving patch:
diff --git a/moto/iam/models.py b/moto/iam/models.py
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -364,6 +364,15 @@ def get_user(self, user_name):
 
         return user
 
+    def list_users(self, path_prefix, marker, max_items):
+        users = None
+        try:
+            users = self.users
+        except KeyError:
+            raise IAMNotFoundException("Users {0}, {1}, {2} not found".format(path_prefix, marker, max_items))
+
+        return users
+
     def create_login_profile(self, user_name, password):
         # This does not currently deal with PasswordPolicyViolation.
         user = self.get_user(user_name)
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -165,6 +165,14 @@ def get_user(self):
         template = self.response_template(USER_TEMPLATE)
         return template.render(action='Get', user=user)
 
+    def list_users(self):
+        path_prefix = self._get_param('PathPrefix')
+        marker = self._get_param('Marker')
+        max_items = self._get_param('MaxItems')
+        user = iam_backend.list_users(path_prefix, marker, max_items)
+        template = self.response_template(LIST_USERS_TEMPLATE)
+        return template.render(action='List', user=user)
+
     def create_login_profile(self):
         user_name = self._get_param('UserName')
         password = self._get_param('Password')
@@ -592,6 +600,24 @@ def get_credential_report(self):
    </ResponseMetadata>
 </{{ action }}UserResponse>"""
 
+LIST_USERS_TEMPLATE = """<{{ action }}UsersResponse>
+   <{{ action }}UsersResult>
+      <Users>
+         {% for user in users %}
+         <member>
+             <UserId>{{ user.id }}</UserId>
+             <Path>{{ user.path }}</Path>
+             <UserName>{{ user.name }}</UserName>
+             <Arn>arn:aws:iam::123456789012:user/{{ user.path }}/{{ user.name }}</Arn>
+         </member>
+         {% endfor %}
+     </Users>
+   </{{ action }}UsersResult>
+   <ResponseMetadata>
+      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
+   </ResponseMetadata>
+</{{ action }}UsersResponse>"""
+
 CREATE_LOGIN_PROFILE_TEMPLATE = """
 <CreateLoginProfileResponse>
    <CreateUserResult>


Test patch:
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
 import boto
+import boto3
 import sure  # noqa
 
 from nose.tools import assert_raises, assert_equals, assert_not_equals
@@ -175,6 +176,18 @@ def test_get_user():
     conn.create_user('my-user')
     conn.get_user('my-user')
 
+@mock_iam()
+def test_list_users():
+    path_prefix = '/'
+    max_items = 10
+    conn = boto3.client('iam')
+    conn.create_user(UserName='my-user')
+    response = conn.list_users(PathPrefix=path_prefix, MaxItems=max_items)
+    assert_equals(
+        response['Users'],
+        []
+    )
+
 
 @mock_iam()
 def test_create_login_profile():