Fix issue when deleting stack contains role with inline policy
diff --git a/localstack/utils/cloudformation/template_deployer.py b/localstack/utils/cloudformation/template_deployer.py
index 3267443af8794..5b8291f94dae5 100644
--- a/localstack/utils/cloudformation/template_deployer.py
+++ b/localstack/utils/cloudformation/template_deployer.py
@@ -1086,9 +1086,22 @@ def deploy_resource(resource_id, resources, stack_name):
 
 
 def delete_resource(resource_id, resources, stack_name):
-    for res_id, res in resources.items():
-        if res['ResourceType'] == 'AWS::S3::Bucket':
-            s3_listener.remove_bucket_notification(res['PhysicalResourceId'])
+    res = resources[resource_id]
+    if res['ResourceType'] == 'AWS::S3::Bucket':
+        s3_listener.remove_bucket_notification(res['PhysicalResourceId'])
+
+    if res['ResourceType'] == 'AWS::IAM::Role':
+        role_name = res['PhysicalResourceId']
+
+        iam_client = aws_stack.connect_to_service('iam')
+        rs = iam_client.list_role_policies(
+            RoleName=role_name
+        )
+        for policy in rs['PolicyNames']:
+            iam_client.delete_role_policy(
+                RoleName=role_name,
+                PolicyName=policy
+            )
 
     return execute_resource_action(resource_id, resources, stack_name, ACTION_DELETE)
 
@@ -1246,7 +1259,6 @@ def delete_stack(stack_name, stack_resources):
 # --------
 # Util methods for analyzing resource dependencies
 # --------
-
 def is_deployable_resource(resource):
     resource_type = get_resource_type(resource)
     entry = RESOURCE_TO_FUNCTION.get(resource_type)
diff --git a/tests/integration/test_cloudformation.py b/tests/integration/test_cloudformation.py
index a6999bd26c9e4..2dd068a091453 100644
--- a/tests/integration/test_cloudformation.py
+++ b/tests/integration/test_cloudformation.py
@@ -604,6 +604,31 @@ def handler(event, context):
       Ref: AWS::Region
 """
 
+TEST_DEPLOY_BODY_4 = """
+AWSTemplateFormatVersion: '2010-09-09'
+
+Resources:
+  # IAM role for running the step function
+  ExecutionRole:
+    Type: "AWS::IAM::Role"
+    Properties:
+      AssumeRolePolicyDocument:
+        Version: "2012-10-17"
+        Statement:
+        - Effect: "Allow"
+          Principal:
+            Service: !Sub states.${AWS::Region}.amazonaws.com
+          Action: "sts:AssumeRole"
+      Policies:
+      - PolicyName: StatesExecutionPolicy
+        PolicyDocument:
+          Version: "2012-10-17"
+          Statement:
+          - Effect: Allow
+            Action: "lambda:InvokeFunction"
+            Resource: "*"
+"""
+
 TEST_TEMPLATE_19 = """
 Conditions:
   IsPRD:
@@ -1228,6 +1253,60 @@ def test_deploy_stack_with_dynamodb_table(self):
         rs = ddb_client.list_tables()
         self.assertNotIn(ddb_table_name, rs['TableNames'])
 
+    def test_deploy_stack_with_iam_nested_policy(self):
+        stack_name = 'stack-%s' % short_uid()
+        change_set_name = 'change-set-%s' % short_uid()
+
+        cloudformation = aws_stack.connect_to_service('cloudformation')
+
+        rs = cloudformation.create_change_set(
+            StackName=stack_name,
+            ChangeSetName=change_set_name,
+            TemplateBody=TEST_DEPLOY_BODY_4
+        )
+
+        self.assertEqual(rs['ResponseMetadata']['HTTPStatusCode'], 200)
+        change_set_id = rs['Id']
+
+        rs = cloudformation.describe_change_set(
+            StackName=stack_name,
+            ChangeSetName=change_set_id
+        )
+        self.assertEqual(rs['ResponseMetadata']['HTTPStatusCode'], 200)
+        self.assertEqual(rs['ChangeSetId'], change_set_id)
+        self.assertEqual(rs['Status'], 'CREATE_COMPLETE')
+
+        iam_client = aws_stack.connect_to_service('iam')
+        rs = iam_client.list_roles()
+        number_of_roles = len(rs['Roles'])
+
+        rs = cloudformation.execute_change_set(
+            StackName=stack_name,
+            ChangeSetName=change_set_name
+        )
+        self.assertEqual(rs['ResponseMetadata']['HTTPStatusCode'], 200)
+
+        rs = cloudformation.describe_stacks(
+            StackName=stack_name
+        )
+        self.assertEqual(rs['ResponseMetadata']['HTTPStatusCode'], 200)
+
+        rs = iam_client.list_roles()
+        # 1 role was created
+        self.assertEqual(number_of_roles + 1, len(rs['Roles']))
+
+        # clean up
+        cloudformation.delete_change_set(
+            StackName=stack_name,
+            ChangeSetName=change_set_name
+        )
+        cloudformation.delete_stack(
+            StackName=stack_name
+        )
+        rs = iam_client.list_roles()
+        # role was removed
+        self.assertEqual(number_of_roles, len(rs['Roles']))
+
     def test_cfn_handle_s3_bucket_resources(self):
         stack_name = 'stack-%s' % short_uid()
         bucket_name = 's3-bucket-%s' % short_uid()

* Delete role polices before deleting IAM::Role resource
* Add integration test

Issue fixed:
#2790 CloudFormation: Delete stack containing IAM role with inline policy fails
