Back to Blog
4 min read

Power Automate Cloud Flows - Building Intelligent Automation

Power Automate cloud flows democratize automation, enabling both citizen developers and professional developers to build sophisticated workflows. Today, I want to share practical patterns and techniques for building robust cloud flows that can transform your business processes.

Understanding Cloud Flow Types

Power Automate offers three types of cloud flows:

  1. Automated flows - Triggered by events (email arrival, file creation)
  2. Instant flows - Triggered manually (button press, Power Apps)
  3. Scheduled flows - Triggered by time (daily, weekly, monthly)

Building Your First Automated Flow

Let’s create a flow that processes incoming invoices from email:

Step 1: Configure the Trigger

trigger:
  type: "When a new email arrives (V3)"
  folder: "Inbox"
  subject_filter: "Invoice"
  has_attachment: true
  include_attachments: true

Step 2: Initialize Variables

actions:
  - Initialize_InvoiceData:
      type: "InitializeVariable"
      name: "InvoiceData"
      type: "Object"
      value: {}

  - Initialize_ProcessingStatus:
      type: "InitializeVariable"
      name: "ProcessingStatus"
      type: "String"
      value: "Pending"

Step 3: Process Attachments

actions:
  - For_each_attachment:
      type: "Foreach"
      items: "@triggerOutputs()?['body/attachments']"
      actions:
        - Condition_Is_PDF:
            type: "If"
            expression: "@endsWith(items('For_each_attachment')?['name'], '.pdf')"
            if_true:
              - Create_blob:
                  type: "CreateBlob"
                  path: "/invoices/@{items('For_each_attachment')?['name']}"
                  content: "@base64ToBinary(items('For_each_attachment')?['contentBytes'])"

              - Extract_with_AI_Builder:
                  type: "AIBuilder_ExtractInvoiceInformation"
                  document: "@body('Create_blob')"

Advanced Expression Techniques

Power Automate expressions enable powerful data transformations:

String Manipulation

// Concatenate strings
concat('Invoice-', formatDateTime(utcNow(), 'yyyyMMdd'), '-', guid())

// Extract substring
substring(variables('InvoiceNumber'), 0, 3)

// Replace text
replace(triggerOutputs()?['body/subject'], 'RE: ', '')

// Split and access array
split(triggerOutputs()?['body/to'], ';')[0]

Date Operations

// Current date formatted
formatDateTime(utcNow(), 'yyyy-MM-dd')

// Add days to date
addDays(utcNow(), 30)

// Calculate difference
div(sub(ticks(variables('EndDate')), ticks(variables('StartDate'))), 864000000000)

// Start of month
startOfMonth(utcNow())

Array Operations

// Filter array
@{filter(body('Get_items')?['value'], item(), equals(item()?['Status'], 'Active'))}

// First item
first(body('Get_items')?['value'])

// Length
length(body('Get_items')?['value'])

// Create array from select
@{select(body('Get_items')?['value'], item()?['Email'])}

Error Handling Patterns

Configure Run After Settings

actions:
  - Try_Action:
      type: "Http"
      inputs:
        method: "POST"
        uri: "https://api.example.com/process"

  - Handle_Success:
      type: "Compose"
      inputs: "Success"
      runAfter:
        Try_Action: ["Succeeded"]

  - Handle_Failure:
      type: "Compose"
      inputs: "Failed: @{actions('Try_Action')?['error']?['message']}"
      runAfter:
        Try_Action: ["Failed", "TimedOut"]

Scope for Try-Catch-Finally

actions:
  - Scope_Try:
      type: "Scope"
      actions:
        - Risky_Operation:
            type: "Http"
            uri: "https://unreliable-api.com"

  - Scope_Catch:
      type: "Scope"
      runAfter:
        Scope_Try: ["Failed"]
      actions:
        - Send_Error_Notification:
            type: "SendEmail"
            to: "admin@company.com"
            subject: "Flow Failed"
            body: "@{result('Scope_Try')}"

  - Scope_Finally:
      type: "Scope"
      runAfter:
        Scope_Try: ["Succeeded", "Failed", "Skipped"]
        Scope_Catch: ["Succeeded", "Failed", "Skipped"]
      actions:
        - Log_Completion:
            type: "Compose"
            inputs: "Flow completed at @{utcNow()}"

Approval Workflows

Create multi-level approval flows:

actions:
  - Start_Approval:
      type: "StartAndWaitForAnApproval"
      approvalType: "Approve/Reject - First to respond"
      title: "Expense Report Approval: @{triggerOutputs()?['body/Title']}"
      assignedTo: "@{triggerOutputs()?['body/Manager/Email']}"
      details: |
        **Employee:** @{triggerOutputs()?['body/Employee/DisplayName']}
        **Amount:** $@{triggerOutputs()?['body/TotalAmount']}
        **Purpose:** @{triggerOutputs()?['body/Purpose']}
      itemLink: "@{triggerOutputs()?['body/Link']}"

  - Condition_Approved:
      type: "If"
      expression: "@equals(outputs('Start_Approval')?['body/outcome'], 'Approve')"
      if_true:
        - Update_Status:
            type: "UpdateItem"
            status: "Approved"
        - Send_Confirmation:
            type: "SendEmail"
            to: "@{triggerOutputs()?['body/Employee/Email']}"
            subject: "Expense Report Approved"
      if_false:
        - Update_Status_Rejected:
            type: "UpdateItem"
            status: "Rejected"
        - Send_Rejection:
            type: "SendEmail"
            to: "@{triggerOutputs()?['body/Employee/Email']}"
            subject: "Expense Report Rejected"
            body: "Reason: @{outputs('Start_Approval')?['body/comments']}"

Integration with Power Apps

Trigger Flow from Power Apps

trigger:
  type: "PowerApps"
  inputs:
    - CustomerName:
        type: "string"
        required: true
    - OrderAmount:
        type: "number"
        required: true

Return Data to Power Apps

actions:
  - Respond_to_PowerApp:
      type: "RespondToPowerApp"
      outputs:
        OrderId: "@{body('Create_Order')?['ID']}"
        ConfirmationNumber: "@{variables('ConfirmationNumber')}"
        Status: "Success"

Performance Optimization

Concurrency Control

trigger:
  type: "Recurrence"
  concurrency:
    runs: 1  # Prevent parallel runs

Pagination for Large Datasets

actions:
  - Get_All_Items:
      type: "GetItems"
      site: "https://company.sharepoint.com/sites/data"
      list: "LargeList"
      settings:
        paging:
          enabled: true
          pageSize: 5000

Filter at Source

actions:
  - Get_Filtered_Items:
      type: "GetItems"
      filterQuery: "Status eq 'Active' and CreatedDate ge '@{addDays(utcNow(), -7)}'"
      topCount: 100

Child Flows for Reusability

Parent Flow

actions:
  - Call_Child_Flow:
      type: "Workflow"
      inputs:
        host:
          workflow:
            id: "/workflows/child-flow-id"
        body:
          inputParam1: "@{variables('Data')}"

Child Flow

trigger:
  type: "Manual"
  inputs:
    inputParam1:
      type: "string"

actions:
  - Process_Data:
      type: "Compose"
      inputs: "Processing: @{triggerBody()?['inputParam1']}"

  - Response:
      type: "Response"
      body:
        result: "@{outputs('Process_Data')}"

Best Practices

  1. Use meaningful names for actions and variables
  2. Implement proper error handling with scopes
  3. Use child flows for reusable logic
  4. Filter data at the source to improve performance
  5. Enable run history for troubleshooting
  6. Use environment variables for configuration
  7. Test thoroughly with the test feature before publishing

Conclusion

Power Automate cloud flows provide a powerful platform for automating business processes. Whether you’re building simple email notifications or complex multi-system integrations, understanding these patterns will help you create robust, maintainable automation solutions. The combination of low-code development with advanced expression capabilities makes it accessible for everyone while still providing the power needed for enterprise scenarios.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.