Repository: getmoto/moto
Pull request #8088 (2024-09-05 16:41:04)

Austoscaling: Ensure CreateDate is in isoformat
Howdy,

When creating an AutoScaling Group the  `CreatedTime` should be in ISO format. At the moment the returned value is causing a deserialization error.

```
│ Error: reading Auto Scaling Group (sandwich): operation error Auto Scaling: DescribeAutoScalingGroups, https response error StatusCode: 200, RequestID: ucKD1cbqn2lqSIvuMlxcMYuaWEyT6kImWKuyBdbUHE7Gr4YZ0VdO, deserialization failed, failed to decode response body, unable to parse time string, parse errors:
│  * "2006-01-02T15:04:05.999999999Z": parsing time "2024-09-06 02:13:02.824378" as "2006-01-02T15:04:05.999999999Z": cannot parse " 02:13:02.824378" as "T"
│  * "2006-01-02T15:04:05.999999999": parsing time "2024-09-06 02:13:02.824378" as "2006-01-02T15:04:05.999999999": cannot parse " 02:13:02.824378" as "T"
│  * "2006-01-02T15:04:05.999999999Z07:00": parsing time "2024-09-06 02:13:02.824378" as "2006-01-02T15:04:05.999999999Z07:00": cannot parse " 02:13:02.824378" as "T"
│  * "2006-01-02T15:04:05Z07:00": parsing time "2024-09-06 02:13:02.824378" as "2006-01-02T15:04:05Z07:00": cannot parse " 02:13:02.824378" as "T"
```

As part of the PR, I included the terraform tests to reproduce the above error with the current version of moto `5.0.13`

```
terraform --version
Terraform v1.9.5
on darwin_arm64
+ provider registry.terraform.io/hashicorp/aws v5.65.0
```

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/8088?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.44%. Comparing base [(`6ff25fc`)](https://app.codecov.io/gh/getmoto/moto/commit/6ff25fc7c03874c5124646f6e8443ec001afad1e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) to head [(`c6b25b1`)](https://app.codecov.io/gh/getmoto/moto/commit/c6b25b1f3f756a6e78531261f20efa61be339b8a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #8088   +/-   ##
=======================================
  Coverage   94.44%   94.44%           
=======================================
  Files        1141     1141           
  Lines       98172    98172           
=======================================
  Hits        92722    92722           
  Misses       5450     5450           
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/8088/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Coverage Δ | |
|---|---|---|
| [servertests](https://app.codecov.io/gh/getmoto/moto/pull/8088/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `28.96% <0.00%> (ø)` | |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/8088/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `94.42% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/getmoto/moto/pull/8088?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).


Patch:
diff --git a/docs/conf.py b/docs/conf.py
index 0c55adfa2..2c28acc06 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,9 +13,6 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys
-import os
-import shlex
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
diff --git a/moto/autoscaling/models.py b/moto/autoscaling/models.py
index 4880b0a4e..241b3eaca 100644
--- a/moto/autoscaling/models.py
+++ b/moto/autoscaling/models.py
@@ -487,7 +487,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
 
         self.metrics: List[str] = []
         self.warm_pool: Optional[FakeWarmPool] = None
-        self.created_time = created_time
+        self.created_time = created_time.isoformat()
 
     @property
     def tags(self) -> List[Dict[str, str]]:
diff --git a/moto/ec2/models/security_groups.py b/moto/ec2/models/security_groups.py
index eac8cf5f2..c9e7b98f8 100644
--- a/moto/ec2/models/security_groups.py
+++ b/moto/ec2/models/security_groups.py
@@ -634,12 +634,17 @@ class SecurityGroupBackend:
         results = []
 
         if sg_rule_ids:
+            rules_sources = [
+                self.sg_old_ingress_ruls.items(),
+                self.sg_old_egress_ruls.items(),
+            ]
             # go thru all the rules in the backend to find a match
             for sg_rule_id in sg_rule_ids:
-                for sg in self.sg_old_ingress_ruls:
-                    for rule in self.sg_old_ingress_ruls[sg]:
-                        if rule.id == sg_rule_id:
-                            results.append(rule)
+                for rules in rules_sources:
+                    for sg, rules_list in rules:
+                        for rule in rules_list:
+                            if rule.id == sg_rule_id:
+                                results.append(rule)
 
             return results
 
diff --git a/moto/stepfunctions/models.py b/moto/stepfunctions/models.py
index d2ea33a56..f8008e0cf 100644
--- a/moto/stepfunctions/models.py
+++ b/moto/stepfunctions/models.py
@@ -45,6 +45,7 @@ class StateMachine(CloudFormationModel):
         self.tags: List[Dict[str, str]] = []
         if tags:
             self.add_tags(tags)
+        self.version = 0
 
     def start_execution(
         self,
@@ -521,6 +522,7 @@ class StepFunctionBackend(BaseBackend):
         definition: str,
         roleArn: str,
         tags: Optional[List[Dict[str, str]]] = None,
+        publish: Optional[bool] = None,
     ) -> StateMachine:
         self._validate_name(name)
         self._validate_role_arn(roleArn)
@@ -529,6 +531,8 @@ class StepFunctionBackend(BaseBackend):
             return self.describe_state_machine(arn)
         except StateMachineDoesNotExist:
             state_machine = StateMachine(arn, name, definition, roleArn, tags)
+            if publish:
+                state_machine.version += 1
             self.state_machines.append(state_machine)
             return state_machine
 
@@ -552,7 +556,13 @@ class StepFunctionBackend(BaseBackend):
             self.state_machines.remove(sm)
 
     def update_state_machine(
-        self, arn: str, definition: Optional[str] = None, role_arn: Optional[str] = None
+        self,
+        arn: str,
+        definition: Optional[str] = None,
+        role_arn: Optional[str] = None,
+        logging_configuration: Optional[Dict[str, bool]] = None,
+        tracing_configuration: Optional[Dict[str, bool]] = None,
+        publish: Optional[bool] = None,
     ) -> StateMachine:
         sm = self.describe_state_machine(arn)
         updates = {
@@ -560,6 +570,8 @@ class StepFunctionBackend(BaseBackend):
             "roleArn": role_arn,
         }
         sm.update(**updates)
+        if publish:
+            sm.version += 1
         return sm
 
     def start_execution(
diff --git a/moto/stepfunctions/parser/models.py b/moto/stepfunctions/parser/models.py
index ca4abf7cc..ca75eff7f 100644
--- a/moto/stepfunctions/parser/models.py
+++ b/moto/stepfunctions/parser/models.py
@@ -14,7 +14,6 @@ from moto.stepfunctions.parser.api import (
     LoggingConfiguration,
     MissingRequiredParameter,
     Name,
-    Publish,
     ResourceNotFound,
     SendTaskFailureOutput,
     SendTaskHeartbeatOutput,
@@ -81,11 +80,16 @@ class StepFunctionsParserBackend(StepFunctionBackend):
         definition: str,
         roleArn: str,
         tags: Optional[List[Dict[str, str]]] = None,
+        publish: Optional[bool] = None,
     ) -> StateMachine:
         StepFunctionsParserBackend._validate_definition(definition=definition)
 
         return super().create_state_machine(
-            name=name, definition=definition, roleArn=roleArn, tags=tags
+            name=name,
+            definition=definition,
+            roleArn=roleArn,
+            tags=tags,
+            publish=publish,
         )
 
     def send_task_heartbeat(self, task_token: TaskToken) -> SendTaskHeartbeatOutput:
@@ -188,7 +192,7 @@ class StepFunctionsParserBackend(StepFunctionBackend):
         role_arn: str = None,
         logging_configuration: LoggingConfiguration = None,
         tracing_configuration: TracingConfiguration = None,
-        publish: Publish = None,
+        publish: Optional[bool] = None,
         version_description: VersionDescription = None,
     ) -> StateMachine:
         if not any(
@@ -201,7 +205,14 @@ class StepFunctionsParserBackend(StepFunctionBackend):
         if definition is not None:
             self._validate_definition(definition=definition)
 
-        return super().update_state_machine(arn, definition, role_arn)
+        return super().update_state_machine(
+            arn,
+            definition,
+            role_arn,
+            logging_configuration=logging_configuration,
+            tracing_configuration=tracing_configuration,
+            publish=publish,
+        )
 
     def describe_map_run(self, map_run_arn: str) -> Dict[str, Any]:
         for execution in self._get_executions():
diff --git a/moto/stepfunctions/responses.py b/moto/stepfunctions/responses.py
index 70633c164..b258b460c 100644
--- a/moto/stepfunctions/responses.py
+++ b/moto/stepfunctions/responses.py
@@ -28,13 +28,22 @@ class StepFunctionResponse(BaseResponse):
         definition = self._get_param("definition")
         roleArn = self._get_param("roleArn")
         tags = self._get_param("tags")
+        publish = self._get_param("publish")
         state_machine = self.stepfunction_backend.create_state_machine(
-            name=name, definition=definition, roleArn=roleArn, tags=tags
+            name=name,
+            definition=definition,
+            roleArn=roleArn,
+            tags=tags,
+            publish=publish,
         )
         response = {
             "creationDate": state_machine.creation_date,
             "stateMachineArn": state_machine.arn,
         }
+        if publish:
+            response["stateMachineVersionArn"] = (
+                f"{state_machine.arn}:{state_machine.version}"
+            )
         return 200, {}, json.dumps(response)
 
     def list_state_machines(self) -> TYPE_RESPONSE:
@@ -83,12 +92,22 @@ class StepFunctionResponse(BaseResponse):
         arn = self._get_param("stateMachineArn")
         definition = self._get_param("definition")
         role_arn = self._get_param("roleArn")
+        tracing_config = self._get_param("tracingConfiguration")
+        publish = self._get_param("publish")
         state_machine = self.stepfunction_backend.update_state_machine(
-            arn=arn, definition=definition, role_arn=role_arn
+            arn=arn,
+            definition=definition,
+            role_arn=role_arn,
+            tracing_configuration=tracing_config,
+            publish=publish,
         )
         response = {
             "updateDate": state_machine.update_date,
         }
+        if publish:
+            response["stateMachineVersionArn"] = (
+                f"{state_machine.arn}:{state_machine.version}"
+            )
         return 200, {}, json.dumps(response)
 
     def list_tags_for_resource(self) -> TYPE_RESPONSE:
diff --git a/other_langs/terraform/autoscaling/data.tf b/other_langs/terraform/autoscaling/data.tf
new file mode 100644
index 000000000..3d71c6420
--- /dev/null
+++ b/other_langs/terraform/autoscaling/data.tf
@@ -0,0 +1,17 @@
+data "aws_caller_identity" "this" {}
+
+data "aws_vpc" "this" {
+  default = true
+}
+
+data "aws_subnets" "this" {
+  filter {
+    name   = "vpc-id"
+    values = [data.aws_vpc.this.id]
+  }
+}
+
+data "aws_subnet" "this" {
+  for_each = toset(data.aws_subnets.this.ids)
+  id       = each.value
+}
diff --git a/other_langs/terraform/autoscaling/main.tf b/other_langs/terraform/autoscaling/main.tf
new file mode 100644
index 000000000..be172a56e
--- /dev/null
+++ b/other_langs/terraform/autoscaling/main.tf
@@ -0,0 +1,33 @@
+resource "tls_private_key" "this" {
+  algorithm = "RSA"
+  rsa_bits  = 4096
+}
+
+resource "aws_key_pair" "this" {
+  key_name   = var.app_name
+  public_key = tls_private_key.this.public_key_openssh
+}
+
+resource "aws_launch_template" "this" {
+  name                   = var.app_name
+  image_id               = var.ami_id
+  instance_type          = var.instance_type
+  key_name               = aws_key_pair.this.key_name
+  vpc_security_group_ids = [aws_security_group.this.id]
+}
+
+resource "aws_autoscaling_group" "this" {
+  name                  = var.app_name
+  desired_capacity      = 10
+  min_size              = 1
+  max_size              = 50
+  health_check_type     = "EC2"
+  vpc_zone_identifier   = [for subnet_id in data.aws_subnet.this : subnet_id.id]
+
+  wait_for_capacity_timeout = 0
+
+  launch_template {
+    id      = aws_launch_template.this.id
+    version = aws_launch_template.this.latest_version
+  }
+}
diff --git a/other_langs/terraform/autoscaling/provider.tf b/other_langs/terraform/autoscaling/provider.tf
new file mode 100644
index 000000000..d14eeaf62
--- /dev/null
+++ b/other_langs/terraform/autoscaling/provider.tf
@@ -0,0 +1,23 @@
+terraform {
+  required_providers {
+    aws = {
+      source = "hashicorp/aws"
+    }
+  }
+}
+
+provider "aws" {
+  region                      = var.aws_region
+  skip_credentials_validation = true
+  skip_metadata_api_check     = true
+  skip_requesting_account_id  = true
+
+  endpoints {
+   autoscaling = "http://localhost:5000"
+    ec2        = "http://localhost:5000"
+    sts        = "http://localhost:5000"
+  }
+
+  access_key = "my-access-key"
+  secret_key = "my-access-key"
+}
diff --git a/other_langs/terraform/autoscaling/security_groups.tf b/other_langs/terraform/autoscaling/security_groups.tf
new file mode 100644
index 000000000..7d09f626c
--- /dev/null
+++ b/other_langs/terraform/autoscaling/security_groups.tf
@@ -0,0 +1,24 @@
+resource "aws_security_group" "this" {
+  name        = var.app_name
+  vpc_id      = data.aws_vpc.this.id
+
+  ingress {
+    description = "SSH ingress"
+    from_port   = 22
+    to_port     = 22
+    protocol    = "tcp"
+    cidr_blocks = [for cidr in data.aws_subnet.this : cidr.cidr_block]
+  }
+
+  egress {
+    from_port   = 0
+    to_port     = 0
+    protocol    = "-1"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+
+  tags = {
+    Name = var.app_name
+  }
+
+}
diff --git a/other_langs/terraform/autoscaling/variables.tf b/other_langs/terraform/autoscaling/variables.tf
new file mode 100644
index 000000000..90f7d1b4f
--- /dev/null
+++ b/other_langs/terraform/autoscaling/variables.tf
@@ -0,0 +1,23 @@
+variable "aws_region" {
+  description = "The AWS Region to use."
+  type        = string
+  default     = "us-east-1"
+}
+
+variable "app_name" {
+  description = "The application name."
+  type        = string
+  default     = "sandwich"
+}
+
+variable "ami_id" {
+  description = "The AMI to use."
+  type        = string
+  default     = "ami-12c6146b"
+}
+
+variable "instance_type" {
+  description = "The instance type to use."
+  type        = string
+  default     = "t2.nano"
+}
diff --git a/update_version_from_git.py b/update_version_from_git.py
index a6f644651..3e26e055a 100644
--- a/update_version_from_git.py
+++ b/update_version_from_git.py
@@ -26,6 +26,7 @@ import os
 import re
 import subprocess
 import sys
+
 from packaging.version import Version
 
 


Test patch:
diff --git a/.github/workflows/tests_terraform_examples.yml b/.github/workflows/tests_terraform_examples.yml
index b24d748f6..82cf15891 100644
--- a/.github/workflows/tests_terraform_examples.yml
+++ b/.github/workflows/tests_terraform_examples.yml
@@ -11,7 +11,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        service: ["acm", "awslambda", "cloudfront", "codebuild", "ds", "elb", "iam", "rds", "route53", "sqs"]
+        service: ["acm", "autoscaling", "awslambda", "cloudfront", "codebuild", "ds", "elb", "iam", "rds", "route53", "sqs"]
 
     steps:
     - uses: actions/checkout@v4
diff --git a/tests/test_autoscaling/test_autoscaling.py b/tests/test_autoscaling/test_autoscaling.py
index 614b75e79..5a040dbc3 100644
--- a/tests/test_autoscaling/test_autoscaling.py
+++ b/tests/test_autoscaling/test_autoscaling.py
@@ -1364,6 +1364,6 @@ def test_sets_created_time():
 
     asgs = conn.describe_auto_scaling_groups()["AutoScalingGroups"]
     assert len(asgs) == 1
-    assert asgs[0]["CreatedTime"] != datetime.strptime(
-        "2013-05-06T17:47:15.107Z", "%Y-%m-%dT%H:%M:%S.%f%z"
+    assert asgs[0]["CreatedTime"].strftime("%Y %m %d") == datetime.now().strftime(
+        "%Y %m %d"
     )
diff --git a/tests/test_ec2/test_security_groups.py b/tests/test_ec2/test_security_groups.py
index 519b5c2c6..2dc3c797b 100644
--- a/tests/test_ec2/test_security_groups.py
+++ b/tests/test_ec2/test_security_groups.py
@@ -684,6 +684,7 @@ def test_create_and_describe_security_grp_rule():
         Description="Test SG", GroupName=sg_name, VpcId=vpc.id
     )
 
+    # check that the default rule is present using filters
     response = client.describe_security_group_rules(
         Filters=[{"Name": "group-id", "Values": [sg["GroupId"]]}]
     )
@@ -692,11 +693,21 @@ def test_create_and_describe_security_grp_rule():
     # Only the default rule is present
     assert len(rules) == 1
 
+    def _verify_egress_rule(rule):
+        assert rule["IsEgress"] is True
+        assert rule["IpProtocol"] == "-1"
+        assert rule["CidrIpv4"] == "0.0.0.0/0"
+        assert "GroupId" in rule
+
     # Test default egress rule content
-    assert rules[0]["IsEgress"] is True
-    assert rules[0]["IpProtocol"] == "-1"
-    assert rules[0]["CidrIpv4"] == "0.0.0.0/0"
-    assert "GroupId" in rules[0]
+    _verify_egress_rule(rules[0])
+
+    # check that the default rule is present using security group rule ids
+    response = client.describe_security_group_rules(
+        SecurityGroupRuleIds=[rules[0]["SecurityGroupRuleId"]]
+    )
+    rules = response["SecurityGroupRules"]
+    _verify_egress_rule(rules[0])
 
 
 @mock_aws
diff --git a/tests/test_stepfunctions/parser/test_stepfunctions.py b/tests/test_stepfunctions/parser/test_stepfunctions.py
index 515400a52..96c8d2402 100644
--- a/tests/test_stepfunctions/parser/test_stepfunctions.py
+++ b/tests/test_stepfunctions/parser/test_stepfunctions.py
@@ -1,11 +1,20 @@
 import json
+from time import sleep
 from unittest import SkipTest
+from uuid import uuid4
 
+import boto3
 import pytest
 
 from moto import settings
 
-from . import aws_verified, verify_execution_result
+from . import (
+    allow_aws_request,
+    aws_verified,
+    sfn_allow_dynamodb,
+    sfn_role_policy,
+    verify_execution_result,
+)
 
 
 @aws_verified
@@ -82,3 +91,60 @@ def test_state_machine_with_json_regex():
         "json_regex",
         exec_input=json.dumps(_input),
     )
+
+
+@aws_verified
+@pytest.mark.aws_verified
+def test_version_is_only_available_when_published():
+    from tests.test_stepfunctions.test_stepfunctions import simple_definition
+
+    iam = boto3.client("iam", region_name="us-east-1")
+    role_name = f"sfn_role_{str(uuid4())[0:6]}"
+    sfn_role = iam.create_role(
+        RoleName=role_name,
+        AssumeRolePolicyDocument=json.dumps(sfn_role_policy),
+        Path="/",
+    )["Role"]["Arn"]
+    iam.put_role_policy(
+        PolicyDocument=json.dumps(sfn_allow_dynamodb),
+        PolicyName="allowLambdaInvoke",
+        RoleName=role_name,
+    )
+    sleep(10 if allow_aws_request() else 0)
+
+    client = boto3.client("stepfunctions", region_name="us-east-1")
+
+    try:
+        name1 = f"sfn_name_{str(uuid4())[0:6]}"
+        response = client.create_state_machine(
+            name=name1, definition=simple_definition, roleArn=sfn_role
+        )
+        assert "stateMachineVersionArn" not in response
+        arn1 = response["stateMachineArn"]
+
+        resp = client.update_state_machine(
+            stateMachineArn=arn1, publish=True, tracingConfiguration={"enabled": True}
+        )
+        assert resp["stateMachineVersionArn"] == f"{arn1}:1"
+
+        resp = client.update_state_machine(
+            stateMachineArn=arn1, tracingConfiguration={"enabled": False}
+        )
+        assert "stateMachineVersionArn" not in resp
+
+        name2 = f"sfn_name_{str(uuid4())[0:6]}"
+        response = client.create_state_machine(
+            name=name2, definition=simple_definition, roleArn=sfn_role, publish=True
+        )
+        arn2 = response["stateMachineArn"]
+        assert response["stateMachineVersionArn"] == f"{arn2}:1"
+
+        resp = client.update_state_machine(
+            stateMachineArn=arn2, publish=True, tracingConfiguration={"enabled": True}
+        )
+        assert resp["stateMachineVersionArn"] == f"{arn2}:2"
+    finally:
+        client.delete_state_machine(stateMachineArn=arn1)
+        client.delete_state_machine(stateMachineArn=arn2)
+        iam.delete_role_policy(RoleName=role_name, PolicyName="allowLambdaInvoke")
+        iam.delete_role(RoleName=role_name)
diff --git a/tests/test_stepfunctions/test_stepfunctions.py b/tests/test_stepfunctions/test_stepfunctions.py
index 3cf112aa5..1f7688ff7 100644
--- a/tests/test_stepfunctions/test_stepfunctions.py
+++ b/tests/test_stepfunctions/test_stepfunctions.py
@@ -3,6 +3,7 @@ import os
 import re
 from datetime import datetime
 from unittest import SkipTest, mock
+from uuid import uuid4
 
 import boto3
 import pytest
@@ -11,6 +12,7 @@ from dateutil.tz import tzutc
 
 from moto import mock_aws
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from tests.test_stepfunctions.parser import sfn_role_policy
 
 region = "us-east-1"
 simple_definition = (
@@ -979,5 +981,47 @@ def test_state_machine_execution_name_limits():
     )
 
 
+@mock_aws
+def test_version_is_only_available_when_published():
+    iam = boto3.client("iam", region_name="us-east-1")
+    role_name = f"sfn_role_{str(uuid4())[0:6]}"
+    sfn_role = iam.create_role(
+        RoleName=role_name,
+        AssumeRolePolicyDocument=json.dumps(sfn_role_policy),
+        Path="/",
+    )["Role"]["Arn"]
+
+    client = boto3.client("stepfunctions", region_name="us-east-1")
+
+    name1 = f"sfn_name_{str(uuid4())[0:6]}"
+    response = client.create_state_machine(
+        name=name1, definition=simple_definition, roleArn=sfn_role
+    )
+    assert "stateMachineVersionArn" not in response
+    arn1 = response["stateMachineArn"]
+
+    resp = client.update_state_machine(
+        stateMachineArn=arn1, publish=True, tracingConfiguration={"enabled": True}
+    )
+    assert resp["stateMachineVersionArn"] == f"{arn1}:1"
+
+    resp = client.update_state_machine(
+        stateMachineArn=arn1, tracingConfiguration={"enabled": False}
+    )
+    assert "stateMachineVersionArn" not in resp
+
+    name2 = f"sfn_name_{str(uuid4())[0:6]}"
+    response = client.create_state_machine(
+        name=name2, definition=simple_definition, roleArn=sfn_role, publish=True
+    )
+    arn2 = response["stateMachineArn"]
+    assert response["stateMachineVersionArn"] == f"{arn2}:1"
+
+    resp = client.update_state_machine(
+        stateMachineArn=arn2, publish=True, tracingConfiguration={"enabled": True}
+    )
+    assert resp["stateMachineVersionArn"] == f"{arn2}:2"
+
+
 def _get_default_role():
     return "arn:aws:iam::" + ACCOUNT_ID + ":role/unknown_sf_role"