Schedule a CloudFlow workflow with Terraform

更新时间:
复制 MD 格式

Terraform is an open-source tool that allows you to safely and efficiently preview, provision, and manage your cloud infrastructure and resources. Although CloudFlow scheduling is implemented using EventBridge and is available in the console, you cannot schedule workflows directly with APIs or Terraform. This topic shows you how to automate workflow scheduling by using Terraform to create the required EventBridge resources.

Note

You can run the sample code in this topic with a few clicks.

Prerequisites

  • Activate CloudFlow.

  • Activate EventBridge.

  • An Alibaba Cloud account has full permissions for all its resources. This poses a security risk if the account's credentials are compromised. We recommend using a RAM user with only the necessary permissions. Create an AccessKey pair for the RAM user to make API calls. For more information, see Create a RAM user and Create an AccessKey.

  • Attach the AliyunFnFFullAccess policy to the RAM user to grant permissions to manage CloudFlow (FNF) resources. For more information, see Manage RAM user permissions.

  • Prepare a Terraform runtime environment. You can choose one of the following methods:

    • Use Terraform in Terraform Explorer: Alibaba Cloud provides Terraform Explorer, an online environment where you can run Terraform commands without installation. This method is suitable for quickly testing and debugging Terraform at no cost.

    • Create resources with Terraform: Cloud Shell comes pre-installed with Terraform and is already configured with your identity credentials. You can run Terraform commands directly in Cloud Shell. This method provides quick, low-cost access to Terraform.

    • Install and configure Terraform on your on-premises machine: This method is suitable for environments with poor network connectivity or when you need a custom development setup.

Important

Use Terraform v0.12.28 or later. To check the version, run the terraform --version command.

Resources used

Schedule a CloudFlow workflow with Terraform

  1. Create a working directory and a main.tf configuration file within it. main.tf is the primary file that defines the resources to deploy.

    variable "region" {
      default = "cn-hangzhou"
    }
    provider "alicloud" {
      region = var.region
    }
    # Define a variable for the resource name prefix.
    variable "name" {
      default = "test-mns"
    }
    # Define a variable for the policy name.
    variable "policy_name" {
      type = string
      description = "The name of the policy."
      default = "test-policy"
    }
    # Define a variable for the role name.
    variable "role_name" {
      type = string
      description = "The role that EventBridge assumes to start a flow execution."
      default = "eb-to-fnf-role"
    }
    # Define a variable for the CloudFlow name.
    variable "flow_name" {
      type = string
      description = "The name of the flow."
      default = "test-flow"
    }
    # Define a variable for the flow description.
    variable "flow_description" {
      default = "For flow_description"
    }
    # Define a variable for the event bus name.
    variable "event_bus_name" {
      type = string
      description = "The name of the event bus."
      default = "test-eventbus1"
    }
    # Define a variable for the event bus description.
    variable "event_bus_description" {
      default = "For event_bus_description"
    }
    # Define a variable for the event source name.
    variable "event_source_name" {
      type = string
      description = "The name of the event source."
      default = "test-eventsource1"
    }
    # Define a variable for the event rule name.
    variable "event_rule_name" {
        type = string
        description = "The name of the event rule."
        default = "test-eventrule1"
    }
    # Define a variable for the custom event target ID.
    variable "target_id" {
        type = string
        description = "The ID of the target."
        default = "test-target1"
    }
    # Get the current Alibaba Cloud account ID.
    data "alicloud_account" "current" {
    }
    # Create a random integer to ensure unique resource names.
    resource "random_integer" "default" {
      min = 10000
      max = 99999
    }
    # Create a RAM policy to define permissions.
    resource "alicloud_ram_policy" "policy_exmaple" {
      # The name of the RAM policy.
      policy_name     = "${var.policy_name}-${random_integer.default.result}"
      # (Optional) Specifies whether to forcibly destroy the resource. Default value: false.
      force           = true 
      # The policy document.
       policy_document = <<EOF
      {
        "Statement": [
          {
            "Action": [
              "fnf:*",        
              "mns:*",         
              "eventbridge:*",       
              "ram:*"  
            ],
            "Effect": "Allow",
            "Resource": [
              "*"
            ]
          }
        ],
          "Version": "1"
      }
      EOF
    }
    # Create a RAM role.
    resource "alicloud_ram_role" "role_example" {
      # The name of the role.
      name     = var.role_name
      # (Optional) Specifies whether to forcibly destroy the resource. Default value: false.
      force           = true 
      # The trust policy of the RAM role.
      document = <<EOF
      {
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Principal": {
              "Service": [
                "fnf.aliyuncs.com"
              ]
            }
          }
        ],
        "Version": "1"
      }
      EOF
    }
    # Attach the created RAM policy to the RAM role to grant the corresponding permissions.
    resource "alicloud_ram_role_policy_attachment" "attach_example" {
      # The name of the permission policy.
      policy_name = alicloud_ram_policy.policy_exmaple.policy_name
      # The type of the permission policy.
      policy_type = alicloud_ram_policy.policy_exmaple.type
      # The name of the RAM role.
      role_name   = alicloud_ram_role.role_example.name
    }
    # Create a CloudFlow resource.
    resource "alicloud_fnf_flow" "flow_example" {
      depends_on = [alicloud_ram_role_policy_attachment.attach_example]
      # (Required) The definition of the flow, which must conform to the Flow Definition Language (FDL) syntax.
      definition  = <<EOF
      Type: StateMachine
      Name: ${var.flow_name}
      SpecVersion: v1
      StartAt: Hello World
      States:
      - Type: Pass
        Name: Hello World
        End: true
      EOF
      # The ARN of the RAM role that Serverless Workflow assumes to execute the flow.
      role_arn    = alicloud_ram_role.role_example.arn
      # The description of the flow.
      description = var.flow_description
      # The name of the flow.
      name        = var.flow_name
      # The type of the flow. Valid values: FDL and DEFAULT.
      type        = "FDL"
    }
    # Create an event bus to receive and route events.
    resource "alicloud_event_bridge_event_bus" "eventbus_example" {
      # The name of the event bus.
      event_bus_name = var.event_bus_name
      # (Optional) The description of the event bus.
      description = var.event_bus_description
    }
    # Create an MNS queue resource.
    resource "alicloud_mns_queue" "example" {
      # The name of the queue.
      name = "${var.name}-${random_integer.default.result}"
      # The delay, in seconds, before a message sent to the queue can be dequeued.
      delay_seconds            = 0
      # The maximum size, in bytes, of a message body.
      maximum_message_size     = 65536
      # The duration, in seconds, for a message to remain in the queue.
      message_retention_period = 345600
      # The visibility timeout of the queue, in seconds.
      visibility_timeout       = 30
      # The long-polling wait time, in seconds.
      polling_wait_seconds     = 0
    }
    # Create an event source to generate scheduled events.
    resource "alicloud_event_bridge_event_source" "eventsource_example" {
      # The name of the event bus.
      event_bus_name         = alicloud_event_bridge_event_bus.eventbus_example.event_bus_name
      # The name of the event source.
      event_source_name      = var.event_source_name
      # (Optional, Computed) Specifies whether to connect to an external data source. Default value: false.
      linked_external_source = true
      # (Optional) The type of the external data source. Valid values: RabbitMQ, RocketMQ, and MNS. Note: This parameter is valid only when linked_external_source is true.
      external_source_type   = "MNS"
      # (Optional, Map) The configuration of the external source.
      external_source_config = {
        QueueName = alicloud_mns_queue.example.name
      }
    }
    # Define a local variable to store the ARN of the CloudFlow flow.
    locals {
        flow_arn = format("acs:fnf:::flow/%s", var.flow_name)
    }
    # Create an event rule to match events generated by the event source and route the events to the specified CloudFlow flow.
    resource "alicloud_event_bridge_rule" "eventrule_example" {
      # The name of the event bus.
      event_bus_name = alicloud_event_bridge_event_bus.eventbus_example.event_bus_name
      # The name of the event rule.
      rule_name      = var.event_rule_name
      # The event-matching pattern in JSON format. For more information about the values, see StringEqual pattern and StringExpression pattern.
      filter_pattern = format("{\"source\":[\"%s\"]}", var.event_source_name)
      # The targets of the rule.
      targets {
        # The custom ID of the event target.
        target_id = var.target_id
        # The endpoint of the event target.
        endpoint  = local.flow_arn
        # The type of the event target.
        type      = "acs.fnf"
        param_list {
          resource_key = "Input"
          form         = "ORIGINAL"
        }
        param_list {
          form         = "CONSTANT"
          resource_key = "FlowName"
          value        = var.flow_name
        }
        param_list {
          form         = "CONSTANT"
          resource_key = "RoleName"
          value        = var.role_name
        }
      }
    }
  2. Run the following command to initialize Terraform.

    terraform init

    The following output indicates that the initialization is successful.

    Initializing the backend...
    Initializing provider plugins...
    - Finding latest version of hashicorp/alicloud...
    - Installing hashicorp/alicloud v1.234.0...
    - Installed hashicorp/alicloud v1.234.0 (signed by HashiCorp)
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.
    Terraform has been successfully initialized!
    You may now begin working with Terraform. Try running "terraform plan" to see
    any changes that are required for your infrastructure. All Terraform commands
    should now work.
    If you ever set or change modules or backend configuration for Terraform,
    rerun this command to reinitialize your working directory. If you forget, other
    commands will detect it and remind you to do so if necessary.
  3. Create an execution plan and preview the changes.

    terraform plan
  4. Run the following command to create the CloudFlow schedule.

    terraform apply

    When prompted, enter yes and press Enter. Wait for the command to complete. The following output indicates that the CloudFlow schedule was created successfully.

    Plan: 9 to add, 0 to change, 0 to destroy.
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
      Enter a value: yes
    random_integer.default: Creating...
    random_integer.default: Creation complete after 0s [id=10***]
    alicloud_ram_policy.policy_exmaple: Creating...
    alicloud_ram_role.role_example: Creating...
    alicloud_mns_queue.example: Creating...
    alicloud_event_bridge_event_bus.eventbus_example: Creating...
    alicloud_mns_queue.example: Creation complete after 0s [id=test-mns-10***]
    alicloud_ram_policy.policy_exmaple: Creation complete after 1s [id=test-policy-10***]
    alicloud_ram_role.role_example: Creation complete after 1s [id=eb-to-fnf-r***]
    alicloud_ram_role_policy_attachment.attach_example: Creating...
    alicloud_ram_role_policy_attachment.attach_example: Creation complete after 0s [id=role:test-policy-10486:Custom:eb-to-f***]
    alicloud_fnf_flow.flow_example: Creating...
    alicloud_fnf_flow.flow_example: Creation complete after 0s [id=test-f***]
    alicloud_event_bridge_event_bus.eventbus_example: Creation complete after 2s [id=test-event***]
    alicloud_event_bridge_rule.eventrule_example: Creating...
    alicloud_event_bridge_event_source.eventsource_example: Creating...
    alicloud_event_bridge_event_source.eventsource_example: Creation complete after 0s [id=test-eventsour***]
    alicloud_event_bridge_rule.eventrule_example: Creation complete after 0s [id=test-eventbus1:test-event***]
    Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
  5. Verify the results.

    Terraform show command

    Run the following command to view the details of the resources that Terraform created:

    terraform show
    shell@Alicloud:~/ens/fnf02$ terraform show
    # alicloud_event_bridge_event_bus.eventbus_example:
    resource "alicloud_event_bridge_event_bus" "eventbus_example" {
        description    = "For event_bus_description"
        event_bus_name = "test-eventbus1"
        id             = "xxx"
    }
    # alicloud_event_bridge_event_source.eventsource_example:
    resource "alicloud_event_bridge_event_source" "eventsource_example" {
        event_bus_name        = "test-eventbus1"
        event_source_name     = "test-eventsource1"
        external_source_config = {
            "QueueName" = "test-mns-10486"
        }
        external_source_type  = "MNS"
        id                    = "test-eventsource1"
        linked_external_source = true
    }
    # alicloud_event_bridge_rule.eventrule_example:
    resource "alicloud_event_bridge_rule" "eventrule_example" {
        event_bus_name = "test-eventbus1"
        filter_pattern = jsonencode(
            {
                source = [
                    "test-eventsource1",
                ]
            }
        )
        id             = "xxx"
        rule_name      = "test-eventrule1"
        status         = "ENABLE"
        targets {
            endpoint            = "xxx"
            push_retry_strategy = "BACKOFF_RETRY"
            target_id           = "test-target1"
            type                = "acs.fnf"
            param_list {
                form         = "CONSTANT"
                resource_key = "FlowName"
            }
        }
    }

    CloudFlow console

    After the resources are created, log on to the CloudFlow console. On the Flows page, verify that a workflow named test-flow has been created.

Clean up resources

When you no longer need the resources managed by Terraform, run the following command to destroy them. For more information about the terraform destroy command, see Common commands.

terraform destroy

Complete example

Note

You can run the sample code with a few clicks.

Sample code

variable "region" {
  default = "cn-hangzhou"
}
provider "alicloud" {
  region = var.region
}
# Define a variable for the resource name prefix.
variable "name" {
  default = "test-mns"
}
# Define a variable for the policy name.
variable "policy_name" {
  type = string
  description = "The name of the policy."
  default = "test-policy"
}
# Define a variable for the role name.
variable "role_name" {
  type = string
  description = "The role that EventBridge assumes to start a flow execution."
  default = "eb-to-fnf-role"
}
# Define a variable for the CloudFlow name.
variable "flow_name" {
  type = string
  description = "The name of the flow."
  default = "test-flow"
}
# Define a variable for the flow description.
variable "flow_description" {
  default = "For flow_description"
}
# Define a variable for the event bus name.
variable "event_bus_name" {
  type = string
  description = "The name of the event bus."
  default = "test-eventbus1"
}
# Define a variable for the event bus description.
variable "event_bus_description" {
  default = "For event_bus_description"
}
# Define a variable for the event source name.
variable "event_source_name" {
  type = string
  description = "The name of the event source."
  default = "test-eventsource1"
}
# Define a variable for the event rule name.
variable "event_rule_name" {
    type = string
    description = "The name of the event rule."
    default = "test-eventrule1"
}
# Define a variable for the custom event target ID.
variable "target_id" {
    type = string
    description = "The ID of the target."
    default = "test-target1"
}
# Get the current Alibaba Cloud account ID.
data "alicloud_account" "current" {
}
# Create a random integer to ensure unique resource names.
resource "random_integer" "default" {
  min = 10000
  max = 99999
}
# Create a RAM policy to define permissions.
resource "alicloud_ram_policy" "policy_exmaple" {
  # The name of the RAM policy.
  policy_name     = "${var.policy_name}-${random_integer.default.result}"
  # (Optional) Specifies whether to forcibly destroy the resource. Default value: false.
  force           = true 
  # The policy document.
   policy_document = <<EOF
  {
    "Statement": [
      {
        "Action": [
          "fnf:*",        
          "mns:*",         
          "eventbridge:*",       
          "ram:*"  
        ],
        "Effect": "Allow",
        "Resource": [
          "*"
        ]
      }
    ],
      "Version": "1"
  }
  EOF
}
# Create a RAM role.
resource "alicloud_ram_role" "role_example" {
  # The name of the role.
  name     = var.role_name
  # (Optional) Specifies whether to forcibly destroy the resource. Default value: false.
  force           = true 
  # The trust policy of the RAM role.
  document = <<EOF
  {
    "Statement": [
      {
        "Action": "sts:AssumeRole",
        "Effect": "Allow",
        "Principal": {
          "Service": [
            "fnf.aliyuncs.com"
          ]
        }
      }
    ],
    "Version": "1"
  }
  EOF
}
# Attach the created RAM policy to the RAM role to grant the corresponding permissions.
resource "alicloud_ram_role_policy_attachment" "attach_example" {
  # The name of the permission policy.
  policy_name = alicloud_ram_policy.policy_exmaple.policy_name
  # The type of the permission policy.
  policy_type = alicloud_ram_policy.policy_exmaple.type
  # The name of the RAM role.
  role_name   = alicloud_ram_role.role_example.name
}
# Create a CloudFlow resource.
resource "alicloud_fnf_flow" "flow_example" {
  depends_on = [alicloud_ram_role_policy_attachment.attach_example]
  # (Required) The definition of the flow, which must conform to the Flow Definition Language (FDL) syntax.
  definition  = <<EOF
  Type: StateMachine
  Name: ${var.flow_name}
  SpecVersion: v1
  StartAt: Hello World
  States:
  - Type: Pass
    Name: Hello World
    End: true
  EOF
  # The ARN of the RAM role that Serverless Workflow assumes to execute the flow.
  role_arn    = alicloud_ram_role.role_example.arn
  # The description of the flow.
  description = var.flow_description
  # The name of the flow.
  name        = var.flow_name
  # The type of the flow. Valid values: FDL and DEFAULT.
  type        = "FDL"
}
# Create an event bus to receive and route events.
resource "alicloud_event_bridge_event_bus" "eventbus_example" {
  # The name of the event bus.
  event_bus_name = var.event_bus_name
  # (Optional) The description of the event bus.
  description = var.event_bus_description
}
# Create an MNS queue resource.
resource "alicloud_mns_queue" "example" {
  # The name of the queue.
  name = "${var.name}-${random_integer.default.result}"
  # The delay, in seconds, before a message sent to the queue can be dequeued.
  delay_seconds            = 0
  # The maximum size, in bytes, of a message body.
  maximum_message_size     = 65536
  # The duration, in seconds, for a message to remain in the queue.
  message_retention_period = 345600
  # The visibility timeout of the queue, in seconds.
  visibility_timeout       = 30
  # The long-polling wait time, in seconds.
  polling_wait_seconds     = 0
}
# Create an event source to generate scheduled events.
resource "alicloud_event_bridge_event_source" "eventsource_example" {
  # The name of the event bus.
  event_bus_name         = alicloud_event_bridge_event_bus.eventbus_example.event_bus_name
  # The name of the event source.
  event_source_name      = var.event_source_name
  # (Optional, Computed) Specifies whether to connect to an external data source. Default value: false.
  linked_external_source = true
  # (Optional) The type of the external data source. Valid values: RabbitMQ, RocketMQ, and MNS. Note: This parameter is valid only when linked_external_source is true.
  external_source_type   = "MNS"
  # (Optional, Map) The configuration of the external source.
  external_source_config = {
    QueueName = alicloud_mns_queue.example.name
  }
}
# Define a local variable to store the ARN of the CloudFlow flow.
locals {
    flow_arn = format("acs:fnf:::flow/%s", var.flow_name)
}
# Create an event rule to match events generated by the event source and route the events to the specified CloudFlow flow.
resource "alicloud_event_bridge_rule" "eventrule_example" {
  # The name of the event bus.
  event_bus_name = alicloud_event_bridge_event_bus.eventbus_example.event_bus_name
  # The name of the event rule.
  rule_name      = var.event_rule_name
  # The event-matching pattern in JSON format. For more information about the values, see StringEqual pattern and StringExpression pattern.
  filter_pattern = format("{\"source\":[\"%s\"]}", var.event_source_name)
  # The targets of the rule.
  targets {
    # The custom ID of the event target.
    target_id = var.target_id
    # The endpoint of the event target.
    endpoint  = local.flow_arn
    # The type of the event target.
    type      = "acs.fnf"
    param_list {
      resource_key = "Input"
      form         = "ORIGINAL"
    }
    param_list {
      form         = "CONSTANT"
      resource_key = "FlowName"
      value        = var.flow_name
    }
    param_list {
      form         = "CONSTANT"
      resource_key = "RoleName"
      value        = var.role_name
    }
  }
}

Find more complete examples in the corresponding product folder within More complete examples.