Repository: localstack/localstack

Issue:
bug: Cannot list roles which have permission boundaries attached
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

When attempting to list roles where permission boundaries are attached the response cannot be serialized 

`aws --endpoint-url=http://localhost:4566 iam list-roles --region ap-southeast-2`
```
An error occurred (InternalError) when calling the ListRoles operation (reached max retries: 2): exception while calling iam.ListRoles: Invalid type when serializing AttachedPermissionsBoundary: '<Element 'member' at 0xffff8275ef20>' cannot be parsed to structure.
```

### Expected Behavior

Expect that all roles are listed

### How are you starting LocalStack?

With a docker-compose file

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

    docker run localstack/localstack

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

* Create an IAM policy
* Create an IAM role which used the policy as a permission boundary
* Attempt to list all roles with `aws --endpoint-url=http://localhost:4566 iam list-roles --region ap-southeast-2`


### Environment

```markdown
- OS: MacOS
- LocalStack: latest
```


### Anything else?

_No response_


Discussion:
Welcome to LocalStack! Thanks for reporting your first issue and our team will be working towards fixing the issue for you or reach out for more background information. We recommend joining our [Slack Community](https://localstack.cloud/contact/) for real-time help and drop a message to LocalStack Pro Support if you are a Pro user! If you are willing to contribute towards fixing this issue, please have a look at our [contributing guidelines](https://github.com/localstack/.github/blob/main/CONTRIBUTING.md) and our [contributing guide](https://docs.localstack.cloud/contributing/).
I can confirm that I'm seeing this behavior as well.  I'm running on Localstack version `2.0.1`.

Resolving patch:
diff --git a/localstack/services/iam/provider.py b/localstack/services/iam/provider.py
--- a/localstack/services/iam/provider.py
+++ b/localstack/services/iam/provider.py
@@ -109,9 +109,6 @@ def create_role(
         result = call_moto(context)
 
         if not request.get("MaxSessionDuration") and result["Role"].get("MaxSessionDuration"):
-            backend = get_iam_backend(context)
-            role = backend.get_role(request["RoleName"])
-            role.max_session_duration = None
             result["Role"].pop("MaxSessionDuration")
 
         if "RoleLastUsed" in result["Role"] and not result["Role"]["RoleLastUsed"]:
@@ -230,6 +227,8 @@ def list_roles(
         response_roles = []
         for moto_role in moto_roles:
             response_role = self.moto_role_to_role_type(moto_role)
+            # Permission boundary should not be a part of the response
+            response_role.pop("PermissionsBoundary", None)
             response_roles.append(response_role)
             if (
                 path_prefix


Test patch:
diff --git a/localstack/testing/snapshots/transformer_utility.py b/localstack/testing/snapshots/transformer_utility.py
--- a/localstack/testing/snapshots/transformer_utility.py
+++ b/localstack/testing/snapshots/transformer_utility.py
@@ -251,7 +251,12 @@ def iam_api():
         """
         :return: array with Transformers, for iam api.
         """
-        return [TransformerUtility.key_value("UserName"), TransformerUtility.key_value("UserId")]
+        return [
+            TransformerUtility.key_value("UserName"),
+            TransformerUtility.key_value("UserId"),
+            TransformerUtility.key_value("RoleId"),
+            TransformerUtility.key_value("RoleName"),
+        ]
 
     @staticmethod
     def transcribe_api():
diff --git a/tests/integration/test_iam.py b/tests/integration/test_iam.py
--- a/tests/integration/test_iam.py
+++ b/tests/integration/test_iam.py
@@ -488,8 +488,6 @@ def test_service_linked_role_name_should_match_aws(
     @pytest.mark.aws_validated
     def test_update_assume_role_policy(self, snapshot, aws_client):
         snapshot.add_transformer(snapshot.transform.iam_api())
-        snapshot.add_transformer(snapshot.transform.resource_name("role_name"))
-        snapshot.add_transformer(snapshot.transform.key_value("RoleId", "role_id"))
 
         policy = {
             "Version": "2012-10-17",
@@ -516,3 +514,74 @@ def test_update_assume_role_policy(self, snapshot, aws_client):
             snapshot.match("updated_policy", result)
         finally:
             aws_client.iam.delete_role(RoleName=role_name)
+
+    @pytest.mark.aws_validated
+    def test_create_describe_role(self, snapshot, aws_client, create_role, cleanups):
+        snapshot.add_transformer(snapshot.transform.iam_api())
+        path_prefix = f"/{short_uid()}/"
+        snapshot.add_transformer(snapshot.transform.regex(path_prefix, "/<path-prefix>/"))
+
+        trust_policy = {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Effect": "Allow",
+                    "Principal": {"Service": "ec2.amazonaws.com"},
+                    "Action": "sts:AssumeRole",
+                }
+            ],
+        }
+
+        role_name = f"role-{short_uid()}"
+        create_role_result = create_role(
+            RoleName=role_name, AssumeRolePolicyDocument=json.dumps(trust_policy), Path=path_prefix
+        )
+        snapshot.match("create_role_result", create_role_result)
+        get_role_result = aws_client.iam.get_role(RoleName=role_name)
+        snapshot.match("get_role_result", get_role_result)
+
+        list_roles_result = aws_client.iam.list_roles(PathPrefix=path_prefix)
+        snapshot.match("list_roles_result", list_roles_result)
+
+    @pytest.mark.aws_validated
+    def test_list_roles_with_permission_boundary(
+        self, snapshot, aws_client, create_role, create_policy, cleanups
+    ):
+        snapshot.add_transformer(snapshot.transform.iam_api())
+        path_prefix = f"/{short_uid()}/"
+        snapshot.add_transformer(snapshot.transform.regex(path_prefix, "/<path-prefix>/"))
+
+        trust_policy = {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Effect": "Allow",
+                    "Principal": {"Service": "ec2.amazonaws.com"},
+                    "Action": "sts:AssumeRole",
+                }
+            ],
+        }
+        permission_boundary = {
+            "Version": "2012-10-17",
+            "Statement": [
+                {"Effect": "Allow", "Action": ["lambda:ListFunctions"], "Resource": ["*"]}
+            ],
+        }
+
+        role_name = f"role-{short_uid()}"
+        policy_name = f"policy-{short_uid()}"
+        result = create_role(
+            RoleName=role_name, AssumeRolePolicyDocument=json.dumps(trust_policy), Path=path_prefix
+        )
+        snapshot.match("created_role", result)
+        policy_arn = create_policy(
+            PolicyName=policy_name, PolicyDocument=json.dumps(permission_boundary)
+        )["Policy"]["Arn"]
+
+        aws_client.iam.put_role_permissions_boundary(
+            RoleName=role_name, PermissionsBoundary=policy_arn
+        )
+        cleanups.append(lambda: aws_client.iam.delete_role_permissions_boundary(RoleName=role_name))
+
+        list_roles_result = aws_client.iam.list_roles(PathPrefix=path_prefix)
+        snapshot.match("list_roles_result", list_roles_result)
diff --git a/tests/integration/test_iam.snapshot.json b/tests/integration/test_iam.snapshot.json
--- a/tests/integration/test_iam.snapshot.json
+++ b/tests/integration/test_iam.snapshot.json
@@ -1,14 +1,10 @@
 {
   "tests/integration/test_iam.py::TestIAMIntegrations::test_update_assume_role_policy": {
-    "recorded-date": "25-08-2022, 10:39:48",
+    "recorded-date": "04-07-2023, 16:48:15",
     "recorded-content": {
       "created_role": {
-        "ResponseMetadata": {
-          "HTTPHeaders": {},
-          "HTTPStatusCode": 200
-        },
         "Role": {
-          "Arn": "arn:aws:iam::111111111111:role/<role_name:1>",
+          "Arn": "arn:aws:iam::111111111111:role/<role-name:1>",
           "AssumeRolePolicyDocument": {
             "Statement": [
               {
@@ -27,8 +23,12 @@
           },
           "CreateDate": "datetime",
           "Path": "/",
-          "RoleId": "<role_id:1>",
-          "RoleName": "<role_name:1>"
+          "RoleId": "<role-id:1>",
+          "RoleName": "<role-name:1>"
+        },
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 200
         }
       },
       "updated_policy": {
@@ -54,5 +54,150 @@
         }
       }
     }
+  },
+  "tests/integration/test_iam.py::TestIAMIntegrations::test_list_roles_with_permission_boundary": {
+    "recorded-date": "04-07-2023, 16:07:42",
+    "recorded-content": {
+      "created_role": {
+        "Role": {
+          "Arn": "arn:aws:iam::111111111111:role/<path-prefix>/<role-name:1>",
+          "AssumeRolePolicyDocument": {
+            "Statement": [
+              {
+                "Action": "sts:AssumeRole",
+                "Effect": "Allow",
+                "Principal": {
+                  "Service": "ec2.amazonaws.com"
+                }
+              }
+            ],
+            "Version": "2012-10-17"
+          },
+          "CreateDate": "datetime",
+          "Path": "/<path-prefix>/",
+          "RoleId": "<role-id:1>",
+          "RoleName": "<role-name:1>"
+        },
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 200
+        }
+      },
+      "list_roles_result": {
+        "IsTruncated": false,
+        "Roles": [
+          {
+            "Arn": "arn:aws:iam::111111111111:role/<path-prefix>/<role-name:1>",
+            "AssumeRolePolicyDocument": {
+              "Statement": [
+                {
+                  "Action": "sts:AssumeRole",
+                  "Effect": "Allow",
+                  "Principal": {
+                    "Service": "ec2.amazonaws.com"
+                  }
+                }
+              ],
+              "Version": "2012-10-17"
+            },
+            "CreateDate": "datetime",
+            "MaxSessionDuration": 3600,
+            "Path": "/<path-prefix>/",
+            "RoleId": "<role-id:1>",
+            "RoleName": "<role-name:1>"
+          }
+        ],
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 200
+        }
+      }
+    }
+  },
+  "tests/integration/test_iam.py::TestIAMIntegrations::test_create_describe_role": {
+    "recorded-date": "04-07-2023, 16:06:47",
+    "recorded-content": {
+      "create_role_result": {
+        "Role": {
+          "Arn": "arn:aws:iam::111111111111:role/<path-prefix>/<role-name:1>",
+          "AssumeRolePolicyDocument": {
+            "Statement": [
+              {
+                "Action": "sts:AssumeRole",
+                "Effect": "Allow",
+                "Principal": {
+                  "Service": "ec2.amazonaws.com"
+                }
+              }
+            ],
+            "Version": "2012-10-17"
+          },
+          "CreateDate": "datetime",
+          "Path": "/<path-prefix>/",
+          "RoleId": "<role-id:1>",
+          "RoleName": "<role-name:1>"
+        },
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 200
+        }
+      },
+      "get_role_result": {
+        "Role": {
+          "Arn": "arn:aws:iam::111111111111:role/<path-prefix>/<role-name:1>",
+          "AssumeRolePolicyDocument": {
+            "Statement": [
+              {
+                "Action": "sts:AssumeRole",
+                "Effect": "Allow",
+                "Principal": {
+                  "Service": "ec2.amazonaws.com"
+                }
+              }
+            ],
+            "Version": "2012-10-17"
+          },
+          "CreateDate": "datetime",
+          "MaxSessionDuration": 3600,
+          "Path": "/<path-prefix>/",
+          "RoleId": "<role-id:1>",
+          "RoleLastUsed": {},
+          "RoleName": "<role-name:1>"
+        },
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 200
+        }
+      },
+      "list_roles_result": {
+        "IsTruncated": false,
+        "Roles": [
+          {
+            "Arn": "arn:aws:iam::111111111111:role/<path-prefix>/<role-name:1>",
+            "AssumeRolePolicyDocument": {
+              "Statement": [
+                {
+                  "Action": "sts:AssumeRole",
+                  "Effect": "Allow",
+                  "Principal": {
+                    "Service": "ec2.amazonaws.com"
+                  }
+                }
+              ],
+              "Version": "2012-10-17"
+            },
+            "CreateDate": "datetime",
+            "MaxSessionDuration": 3600,
+            "Path": "/<path-prefix>/",
+            "RoleId": "<role-id:1>",
+            "RoleName": "<role-name:1>"
+          }
+        ],
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 200
+        }
+      }
+    }
   }
 }