diff --git a/aws/resource_aws_sfn_state_machine.go b/aws/resource_aws_sfn_state_machine.go index bbeb5ed6e34..81825478563 100644 --- a/aws/resource_aws_sfn_state_machine.go +++ b/aws/resource_aws_sfn_state_machine.go @@ -6,7 +6,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/sfn" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -54,6 +53,16 @@ func resourceAwsSfnStateMachine() *schema.Resource { Computed: true, }, "tags": tagsSchema(), + "type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: sfn.StateMachineTypeStandard, + ValidateFunc: validation.StringInSlice([]string{ + sfn.StateMachineTypeStandard, + sfn.StateMachineTypeExpress, + }, false), + }, }, } } @@ -66,20 +75,25 @@ func resourceAwsSfnStateMachineCreate(d *schema.ResourceData, meta interface{}) Definition: aws.String(d.Get("definition").(string)), Name: aws.String(d.Get("name").(string)), RoleArn: aws.String(d.Get("role_arn").(string)), + Type: aws.String(d.Get("type").(string)), Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().SfnTags(), } - var activity *sfn.CreateStateMachineOutput + var stateMachine *sfn.CreateStateMachineOutput err := resource.Retry(5*time.Minute, func() *resource.RetryError { var err error - activity, err = conn.CreateStateMachine(params) + stateMachine, err = conn.CreateStateMachine(params) if err != nil { // Note: the instance may be in a deleting mode, hence the retry // when creating the step function. This can happen when we are // updating the resource (since there is no update API call). - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "StateMachineDeleting" { + if isAWSErr(err, sfn.ErrCodeStateMachineDeleting, "") { + return resource.RetryableError(err) + } + //This is done to deal with IAM eventual consistency + if isAWSErr(err, "AccessDeniedException", "") { return resource.RetryableError(err) } @@ -89,14 +103,14 @@ func resourceAwsSfnStateMachineCreate(d *schema.ResourceData, meta interface{}) return nil }) if isResourceTimeoutError(err) { - activity, err = conn.CreateStateMachine(params) + stateMachine, err = conn.CreateStateMachine(params) } if err != nil { return fmt.Errorf("Error creating Step Function State Machine: %s", err) } - d.SetId(*activity.StateMachineArn) + d.SetId(*stateMachine.StateMachineArn) return resourceAwsSfnStateMachineRead(d, meta) } @@ -110,11 +124,9 @@ func resourceAwsSfnStateMachineRead(d *schema.ResourceData, meta interface{}) er }) if err != nil { - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "NotFoundException" || awserr.Code() == "StateMachineDoesNotExist" { - d.SetId("") - return nil - } + if isAWSErr(err, sfn.ErrCodeStateMachineDoesNotExist, "") { + d.SetId("") + return nil } return err } @@ -123,6 +135,7 @@ func resourceAwsSfnStateMachineRead(d *schema.ResourceData, meta interface{}) er d.Set("name", sm.Name) d.Set("role_arn", sm.RoleArn) d.Set("status", sm.Status) + d.Set("type", sm.Type) if err := d.Set("creation_date", sm.CreationDate.Format(time.RFC3339)); err != nil { log.Printf("[DEBUG] Error setting creation_date: %s", err) @@ -155,7 +168,7 @@ func resourceAwsSfnStateMachineUpdate(d *schema.ResourceData, meta interface{}) log.Printf("[DEBUG] Updating Step Function State Machine: %#v", params) if err != nil { - if isAWSErr(err, "StateMachineDoesNotExist", "State Machine Does Not Exist") { + if isAWSErr(err, sfn.ErrCodeStateMachineDoesNotExist, "State Machine Does Not Exist") { return fmt.Errorf("Error updating Step Function State Machine: %s", err) } return err diff --git a/aws/resource_aws_sfn_state_machine_test.go b/aws/resource_aws_sfn_state_machine_test.go index beec17c2dbd..9b2c53c1df2 100644 --- a/aws/resource_aws_sfn_state_machine_test.go +++ b/aws/resource_aws_sfn_state_machine_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/sfn" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -14,7 +13,8 @@ import ( ) func TestAccAWSSfnStateMachine_createUpdate(t *testing.T) { - name := acctest.RandString(10) + resourceName := "aws_sfn_state_machine.test" + rName := acctest.RandomWithPrefix("tf-acc") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -22,34 +22,74 @@ func TestAccAWSSfnStateMachine_createUpdate(t *testing.T) { CheckDestroy: testAccCheckAWSSfnStateMachineDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSfnStateMachineConfig(name, 5), + Config: testAccAWSSfnStateMachineConfig(rName, 5), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSfnExists("aws_sfn_state_machine.foo"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "status", sfn.StateMachineStatusActive), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "name"), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "creation_date"), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "definition"), - resource.TestMatchResourceAttr("aws_sfn_state_machine.foo", "definition", regexp.MustCompile(`.*\"MaxAttempts\": 5.*`)), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "role_arn"), + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "status", sfn.StateMachineStatusActive), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "type", sfn.StateMachineTypeStandard), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "definition"), + resource.TestMatchResourceAttr(resourceName, "definition", regexp.MustCompile(`.*\"MaxAttempts\": 5.*`)), + testAccMatchResourceAttrGlobalARN(resourceName, "role_arn", "iam", regexp.MustCompile(`role/.+`)), ), }, { - Config: testAccAWSSfnStateMachineConfig(name, 10), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSfnStateMachineConfig(rName, 10), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSfnExists("aws_sfn_state_machine.foo"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "status", sfn.StateMachineStatusActive), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "name"), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "creation_date"), - resource.TestMatchResourceAttr("aws_sfn_state_machine.foo", "definition", regexp.MustCompile(`.*\"MaxAttempts\": 10.*`)), - resource.TestCheckResourceAttrSet("aws_sfn_state_machine.foo", "role_arn"), + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "status", sfn.StateMachineStatusActive), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "type", sfn.StateMachineTypeStandard), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestMatchResourceAttr(resourceName, "definition", regexp.MustCompile(`.*\"MaxAttempts\": 10.*`)), + testAccMatchResourceAttrGlobalARN(resourceName, "role_arn", "iam", regexp.MustCompile(`role/.+`)), ), }, }, }) } +func TestAccAWSSfnStateMachine_type_express(t *testing.T) { + resourceName := "aws_sfn_state_machine.test" + rName := acctest.RandomWithPrefix("tf-acc") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSfnStateMachineDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSfnStateMachineConfigType(rName, "EXPRESS"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "type", sfn.StateMachineTypeExpress)), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSfnStateMachineConfigType(rName, "STANDARD"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "type", sfn.StateMachineTypeStandard)), + }, + }, + }) +} + func TestAccAWSSfnStateMachine_Tags(t *testing.T) { - name := acctest.RandString(10) + resourceName := "aws_sfn_state_machine.test" + rName := acctest.RandomWithPrefix("tf-acc") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -57,30 +97,60 @@ func TestAccAWSSfnStateMachine_Tags(t *testing.T) { CheckDestroy: testAccCheckAWSSfnStateMachineDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSfnStateMachineConfigTags1(name, "key1", "value1"), + Config: testAccAWSSfnStateMachineConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSfnStateMachineConfigTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSfnExists("aws_sfn_state_machine.foo"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.key1", "value1"), + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, { - Config: testAccAWSSfnStateMachineConfigTags2(name, "key1", "value1updated", "key2", "value2"), + Config: testAccAWSSfnStateMachineConfigTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSfnExists("aws_sfn_state_machine.foo"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.%", "2"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.key1", "value1updated"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.key2", "value2"), + testAccCheckAWSSfnExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, + }, + }) +} + +func TestAccAWSSfnStateMachine_disappears(t *testing.T) { + var sm sfn.DescribeStateMachineOutput + + resourceName := "aws_sfn_state_machine.test" + rName := acctest.RandomWithPrefix("tf-acc") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSfnStateMachineDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSSfnStateMachineConfigTags1(name, "key2", "value2"), + Config: testAccAWSSfnStateMachineConfig(rName, 5), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSfnExists("aws_sfn_state_machine.foo"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_sfn_state_machine.foo", "tags.key2", "value2"), + testAccCheckAWSSfnExists(resourceName), + testAccCheckAWSSfnStateMachineDisappears(&sm), ), }, + { + ExpectNonEmptyPlan: true, + }, }, }) } @@ -119,7 +189,7 @@ func testAccCheckAWSSfnStateMachineDestroy(s *terraform.State) error { }) if err != nil { - if wserr, ok := err.(awserr.Error); ok && wserr.Code() == "StateMachineDoesNotExist" { + if isAWSErr(err, sfn.ErrCodeStateMachineDoesNotExist, "") { return nil } return err @@ -135,32 +205,22 @@ func testAccCheckAWSSfnStateMachineDestroy(s *terraform.State) error { return fmt.Errorf("Default error in Step Function Test") } -func testAccAWSSfnStateMachineConfig(rName string, rMaxAttempts int) string { - return fmt.Sprintf(` -data "aws_partition" "current" {} +func testAccCheckAWSSfnStateMachineDisappears(sm *sfn.DescribeStateMachineOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).sfnconn -data "aws_region" "current" {} + input := &sfn.DeleteStateMachineInput{ + StateMachineArn: sm.StateMachineArn, + } -resource "aws_iam_role_policy" "iam_policy_for_lambda" { - name = "iam_policy_for_lambda_%s" - role = "${aws_iam_role.iam_for_lambda.id}" + _, err := conn.DeleteStateMachine(input) - policy = <