feat: Add Microsoft Azure integration#3074
feat: Add Microsoft Azure integration#3074Tamiru-Alemnew wants to merge 12 commits intosuperplanehq:mainfrom
Conversation
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
…ctions - Add webhook secret verification in HandleWebhook - Refactor trigger to use helper functions from webhook_events.go - Fix Go formatting issues - Note: Event Grid validation response body limitation documented (framework limitation) Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>
e6232d9 to
6d15ed4
Compare
| // For now, if a SAS token is present, we'll allow it (this is a basic implementation) | ||
| ctx.Logger.Debug("Azure Event Grid SAS token found in header") | ||
| return nil | ||
| } |
There was a problem hiding this comment.
SAS token header bypasses webhook secret authentication
High Severity
When a webhook secret is configured, the authenticateWebhook method allows any request that includes an aeg-sas-token header, regardless of the header's value. An attacker can bypass the secret check entirely by sending any arbitrary value in that header. The SAS token is never validated — the code just checks for its presence and returns nil.
| // Check for custom secret header | ||
| secretHeader := ctx.Headers.Get("X-Webhook-Secret") | ||
| if secretHeader != "" { | ||
| if secretHeader != string(secret) { |
There was a problem hiding this comment.
Webhook secret compared without constant-time equality
Medium Severity
The webhook secret comparisons use plain != string comparison instead of crypto/subtle.ConstantTimeCompare, making them vulnerable to timing side-channel attacks. Other integrations in this codebase (gitlab, render, slack) consistently use constant-time comparison for secret validation.
Additional Locations (1)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| if config.ResourceGroup != "" && resourceGroup != config.ResourceGroup { | ||
| ctx.Logger.Debugf("Skipping VM event for resource group %s (filter: %s)", resourceGroup, config.ResourceGroup) | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Case-sensitive resource group comparison breaks Azure filtering
Medium Severity
The resource group filter uses a case-sensitive comparison (resourceGroup != config.ResourceGroup), but Azure resource group names are case-insensitive. Azure ARM may return different casing in resource IDs than what the user originally specified, causing valid events to be silently filtered out. This comparison needs strings.EqualFold instead.
|
@Tamiru-Alemnew hey, great start! I see that you included the SDK into the application. Is this necessary? Looping in @lucaspin as well. |
Hi @shiroyasha @lucaspin, Great question I was intentional about pulling in the Azure SDK modules ( While it's true we keep the AWS integration lightweight (mostly using the SDK Core + Signer), Azure presents three specific challenges that make the official SDK the safer choice here: 1. Authentication Complexity (OIDC to ARM) Re-implementing this handshake manually—including securely reading the token file, performing the token exchange, caching, and automatic refreshing—would be significant custom code and a potential security risk. The SDK handles this standard flow out of the box. 2. Long-Running Operations (LROs)
3. Complex Typed Resources Mitigation I believe this approach strikes the right balance between binary size and maintainability for the initial integration. |
Signed-off-by: Tamiru Alemnew <tamirualemnew33@gmail.com>


Implements #2921
Summary
This PR adds the Microsoft Azure integration to SuperPlane, including one trigger (
onVirtualMachineCreated) and one action (createVirtualMachine).It uses Azure Workload Identity Federation (OIDC) for authentication, supports implicit NIC creation from a subnet, and returns rich connection details (Public IP, Private IP, Admin Username) after VM creation.
Backend Implementation
pkg/integrations/azureand registered it in the main server.azidentity.NewClientAssertionCredentialandAZURE_FEDERATED_TOKEN_FILE, with a seamless fallback toaz loginfor local development (no client secrets are stored).createVirtualMachineaction:Premium_LRS,StandardSSD_LRS).BeginCreateOrUpdate+PollUntilDone) to wait for the VM to be ready.vmId,privateIp,publicIp, andadminUsername.onVirtualMachineCreatedtrigger:Microsoft.Resources.ResourceWriteSuccessevents.Microsoft.EventGrid.SubscriptionValidationEvent).Frontend Implementation
web_src/src/pages/workflowv2/mappers/azureto wire thecreateVirtualMachineaction andonVirtualMachineCreatedtrigger into the workflow builder."azure"renders as Azure in the UI.Docs
pkg/integrations/azure/README.mdand generated the user-facing docdocs/components/Microsoft Azure.mdxviamake gen.components.docs.az login).onVirtualMachineCreatedtrigger using Azure Event Grid.createVirtualMachineaction, including:Contributoron the target Resource Group).Tests
pkg/integrations/azureto cover:make testand linting pass locally.Notes
createVirtualMachineaction currently targets the Azure Compute + Network APIs via the official Go SDK; support for additional advanced VM options can be added in future PRs if needed.localhost.run) so Azure can reach the webhook endpoints.Demo
onVirtualMachineCreated.createVirtualMachine.