Repository: getmoto/moto

Issue:
Create Change Set creates the CloudFormation Stack in 'CREATE_COMPLETE' status
I am having an issue where when I use the boto3 cloudformation client to create a change set, it seems like that action also creates the stack behind that changeset and sets it to a `CREATE_COMPLETE` status instead of a `REVIEW_IN_PROGRESS` status. 

According to the [boto3 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.create_change_set) and the [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html#w2ab1c15c15c17c11):

```
If you create a change set for a new stack, AWS Cloudformation creates a stack with a unique stack ID, but no template or resources. The stack will be in the REVIEW_IN_PROGRESS  state until you execute the change set.
```

While the stack created under moto does not have any resources, the status seems to be incorrect.

This is a blocker for me as I am trying to code up a framework that implements [waiters](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Waiter.StackCreateComplete) but having an unexpected status code for my stack causes my tests to fail.

I've tried to trace through the [moto](https://github.com/spulec/moto/blob/master/moto/cloudformation/models.py#L23) code to see how I can make this work on my end but I seem to not really be getting anywhere as every logical thing I try to do (add parameters, maybe add tags, etc) seems to give me the same results.

Using: moto 1.3.13, boto 2.49.0, boto3 1.9.214, botocore 1.12.214, troposphere 2.5.0, Python 3.7.4, pytest-5.0.1 -- all installed via pip and all being run within VSCode via pytest

In moto I am using python mocks
Running on MacOS Mojave

Reproducible code sample:
```python
from unittest import TestCase
import boto3
import moto
from troposphere import Template
from troposphere.elasticloadbalancingv2 import (
    LoadBalancer as LoadBalancerv2, Listener, Action, RedirectConfig, Certificate)

class TestMinimalECSService(TestCase):

    @moto.mock_elbv2
    @moto.mock_ec2
    @moto.mock_cloudformation
    def test_changeset_creation(self):
        ec2 = boto3.resource('ec2', region_name='us-west-2')
        vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16')
        sg01 = ec2.create_security_group(
            GroupName='sg01', Description='Test security group sg01', VpcId=vpc.id)
        subnet = ec2.create_subnet(
            VpcId=vpc.id, CidrBlock="10.0.0.0/16", AvailabilityZone='us-west-2a')
        template = template = Template()
        template.add_resource(LoadBalancerv2(
            'LoadBalancer',
            Type='application',
            Scheme='internal',
            Subnets=[subnet.id],
            SecurityGroups=[sg01.id]
        ))
        template.add_resource(Listener(
            'Listener',
            DefaultActions=[
                Action(
                    Type='redirect',
                    RedirectConfig=RedirectConfig(
                        Port='443',
                        Protocol='HTTPS',
                        StatusCode='HTTP_301'
                    )
                )
            ],
            Certificates=[
                Certificate(CertificateArn='something')
            ],
            Port=443,
            Protocol='HTTPS',
            LoadBalancerArn=Ref('LoadBalancer')
        ))
        change_set_name = 'my-change-set-name'
        stack_name = 'my-test-stack-name'
        client = boto3.client('cloudformation', region_name='us-west-2')
        create_change_set_response = client.create_change_set(
            ChangeSetName=change_set_name,
            ChangeSetType='CREATE',
            StackName=stack_name,
            TemplateBody=template.to_json()
        )
        describe_change_set_response = client.describe_change_set(
            ChangeSetName=change_set_name, StackName=stack_name
        )
        describe_stacks_response = client.describe_stacks(StackName=stack_name)
        self.assertTrue(True)

```

`describe_change_set_response['Status']` is `CREATE_COMPLETE`. This is expected.

`describe_stacks_response['Stacks'][0]['StackStatus']` is also 'CREATE_COMPLETE'. This is not expected. I am expecting that the status is `REVIEW_IN_PROGRESS` because I have not yet attempted to execute the change set. 



Discussion:
Thanks; setting this as a feature request to implement the logic for the correct behavior.

Resolving patch:
diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py
--- a/moto/cloudformation/models.py
+++ b/moto/cloudformation/models.py
@@ -239,8 +239,11 @@ def __init__(
         self.cross_stack_resources = cross_stack_resources or {}
         self.resource_map = self._create_resource_map()
         self.output_map = self._create_output_map()
-        self._add_stack_event("CREATE_COMPLETE")
-        self.status = "CREATE_COMPLETE"
+        if create_change_set:
+            self.status = "REVIEW_IN_PROGRESS"
+        else:
+            self.create_resources()
+            self._add_stack_event("CREATE_COMPLETE")
         self.creation_time = datetime.utcnow()
 
     def _create_resource_map(self):
@@ -253,7 +256,7 @@ def _create_resource_map(self):
             self.template_dict,
             self.cross_stack_resources,
         )
-        resource_map.create()
+        resource_map.load()
         return resource_map
 
     def _create_output_map(self):
@@ -326,6 +329,10 @@ def stack_outputs(self):
     def exports(self):
         return self.output_map.exports
 
+    def create_resources(self):
+        self.resource_map.create()
+        self.status = "CREATE_COMPLETE"
+
     def update(self, template, role_arn=None, parameters=None, tags=None):
         self._add_stack_event(
             "UPDATE_IN_PROGRESS", resource_status_reason="User Initiated"
@@ -640,6 +647,7 @@ def execute_change_set(self, change_set_name, stack_name=None):
         else:
             stack._add_stack_event("UPDATE_IN_PROGRESS")
             stack._add_stack_event("UPDATE_COMPLETE")
+        stack.create_resources()
         return True
 
     def describe_stacks(self, name_or_stack_id):
diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py
--- a/moto/cloudformation/parsing.py
+++ b/moto/cloudformation/parsing.py
@@ -529,14 +529,16 @@ def load_conditions(self):
         for condition_name in self.lazy_condition_map:
             self.lazy_condition_map[condition_name]
 
-    def create(self):
+    def load(self):
         self.load_mapping()
         self.transform_mapping()
         self.load_parameters()
         self.load_conditions()
 
+    def create(self):
         # Since this is a lazy map, to create every object we just need to
         # iterate through self.
+        # Assumes that self.load() has been called before
         self.tags.update(
             {
                 "aws:cloudformation:stack-name": self.get("AWS::StackName"),


Test patch:
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
--- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
@@ -835,8 +835,10 @@ def test_describe_change_set():
     )
 
     stack = cf_conn.describe_change_set(ChangeSetName="NewChangeSet")
+
     stack["ChangeSetName"].should.equal("NewChangeSet")
     stack["StackName"].should.equal("NewStack")
+    stack["Status"].should.equal("REVIEW_IN_PROGRESS")
 
     cf_conn.create_change_set(
         StackName="NewStack",
@@ -851,15 +853,30 @@ def test_describe_change_set():
 
 
 @mock_cloudformation
+@mock_ec2
 def test_execute_change_set_w_arn():
     cf_conn = boto3.client("cloudformation", region_name="us-east-1")
+    ec2 = boto3.client("ec2", region_name="us-east-1")
+    # Verify no instances exist at the moment
+    ec2.describe_instances()["Reservations"].should.have.length_of(0)
+    # Create a Change set, and verify no resources have been created yet
     change_set = cf_conn.create_change_set(
         StackName="NewStack",
         TemplateBody=dummy_template_json,
         ChangeSetName="NewChangeSet",
         ChangeSetType="CREATE",
     )
+    ec2.describe_instances()["Reservations"].should.have.length_of(0)
+    cf_conn.describe_change_set(ChangeSetName="NewChangeSet")["Status"].should.equal(
+        "REVIEW_IN_PROGRESS"
+    )
+    # Execute change set
     cf_conn.execute_change_set(ChangeSetName=change_set["Id"])
+    # Verify that the status has changed, and the appropriate resources have been created
+    cf_conn.describe_change_set(ChangeSetName="NewChangeSet")["Status"].should.equal(
+        "CREATE_COMPLETE"
+    )
+    ec2.describe_instances()["Reservations"].should.have.length_of(1)
 
 
 @mock_cloudformation