You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET Core后端中调用PowerShell脚本?附Azure虚拟机构建场景Workshop解决方案

Hey there! Let's tackle your two related needs step by step—first, how to call PowerShell scripts from a .NET Core backend, then we'll build a complete Workshop-ready solution for deploying Azure VMs with a button click using templates. Let's get into it!

1. .NET Core后端调用PowerShell脚本的核心实现

First, you'll need the right NuGet package to interact with PowerShell in .NET Core. Grab the Microsoft.PowerShell.SDK package—it's cross-platform and officially supported, so it works seamlessly on Windows, Linux, and macOS.

基础同步调用示例

Here's a straightforward snippet that runs a PowerShell script file and captures both output and errors:

using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;

public class PowerShellExecutor
{
    public string RunScript(string scriptPath, params string[] arguments)
    {
        // Create a runspace to host PowerShell execution
        using var runspace = RunspaceFactory.CreateRunspace();
        runspace.Open();

        // Initialize a PowerShell instance
        using var ps = PowerShell.Create();
        ps.Runspace = runspace;

        // Add the script file and any required arguments
        ps.AddCommand(scriptPath);
        foreach (var arg in arguments)
        {
            ps.AddArgument(arg);
        }

        // Execute the script and collect results
        var results = ps.Invoke();
        var outputBuilder = new StringBuilder();

        // Process successful output
        foreach (var result in results)
        {
            outputBuilder.AppendLine(result.ToString());
        }

        // Capture and append any errors
        if (ps.Streams.Error.Count > 0)
        {
            outputBuilder.AppendLine("=== Errors Encountered ===");
            foreach (var error in ps.Streams.Error)
            {
                outputBuilder.AppendLine(error.ToString());
            }
        }

        return outputBuilder.ToString();
    }
}

异步版本(适合Web场景)

For web backends, async execution is critical to avoid blocking threads. Here's how to adjust the code for async/await:

public async Task<string> RunScriptAsync(string scriptPath, params string[] arguments)
{
    using var runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();

    using var ps = PowerShell.Create();
    ps.Runspace = runspace;
    ps.AddCommand(scriptPath);
    foreach (var arg in arguments)
    {
        ps.AddArgument(arg);
    }

    // Execute asynchronously
    var results = await ps.InvokeAsync().ConfigureAwait(false);
    var outputBuilder = new StringBuilder();

    // Same output/error handling as before
    foreach (var result in results)
    {
        outputBuilder.AppendLine(result.ToString());
    }

    if (ps.Streams.Error.Count > 0)
    {
        outputBuilder.AppendLine("=== Errors Encountered ===");
        foreach (var error in ps.Streams.Error)
        {
            outputBuilder.AppendLine(error.ToString());
        }
    }

    return outputBuilder.ToString();
}

Key Gotchas to Remember

  • Execution Policy: On Windows, you may need to run Set-ExecutionPolicy RemoteSigned (as admin) to allow script execution. On Linux/macOS, grant execute permissions with chmod +x your-script.ps1.
  • Security: Never hardcode sensitive data (like Azure credentials) in scripts or .NET code—use environment variables or Azure Key Vault instead.
  • Cross-Platform Compatibility: Ensure your PowerShell scripts avoid Windows-specific cmdlets if you're deploying to Linux/macOS backends.
2. Workshop实践:点击按钮部署Azure虚拟机的完整方案

Now let's build the end-to-end scenario: a button click triggers a .NET Core API, which runs a PowerShell script to deploy an Azure VM using an ARM template. This is perfect for a Workshop where participants can learn infrastructure-as-code workflows hands-on.

整体架构

  • Frontend: Simple HTML page with a button that calls the .NET API via JavaScript.
  • Backend: .NET Core Web API endpoint that invokes the PowerShell deployment script.
  • PowerShell Script: Uses Azure PowerShell (Az module) to authenticate with Azure and deploy the ARM template.
  • Azure Setup: A service principal with permissions to create VMs, and a reusable ARM template for VM deployment.

Step 1: Prepare Azure Resources & ARM Template

First, create a minimal ARM template (vm-template.json) to define your VM and related resources:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "vmName": { "type": "string", "defaultValue": "WorkshopVM" },
    "adminUsername": { "type": "string", "defaultValue": "workshopadmin" },
    "adminPassword": { "type": "securestring" },
    "location": { "type": "string", "defaultValue": "[resourceGroup().location]" }
  },
  "resources": [
    {
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2023-07-01",
      "name": "[parameters('vmName')]",
      "location": "[parameters('location')]",
      "properties": {
        "hardwareProfile": { "vmSize": "Standard_D2s_v3" },
        "osProfile": {
          "computerName": "[parameters('vmName')]",
          "adminUsername": "[parameters('adminUsername')]",
          "adminPassword": "[parameters('adminPassword')]"
        },
        "storageProfile": {
          "imageReference": {
            "publisher": "MicrosoftWindowsServer",
            "offer": "WindowsServer",
            "sku": "2022-Datacenter",
            "version": "latest"
          },
          "osDisk": { "createOption": "FromImage" }
        },
        "networkProfile": {
          "networkInterfaces": [
            { "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(parameters('vmName'), '-nic'))]" }
          ]
        }
      }
    },
    {
      "type": "Microsoft.Network/networkInterfaces",
      "apiVersion": "2023-05-01",
      "name": "[concat(parameters('vmName'), '-nic')]",
      "location": "[parameters('location')]",
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              "privateIPAllocationMethod": "Dynamic",
              "subnet": { "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', concat(parameters('vmName'), '-vnet'), 'default')]" }
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Network/virtualNetworks",
      "apiVersion": "2023-05-01",
      "name": "[concat(parameters('vmName'), '-vnet')]",
      "location": "[parameters('location')]",
      "properties": {
        "addressSpace": { "addressPrefixes": ["10.0.0.0/16"] },
        "subnets": [{ "name": "default", "properties": { "addressPrefix": "10.0.0.0/24" } }]
      }
    }
  ]
}

Next, create an Azure service principal for automated authentication (run this in PowerShell):

az ad sp create-for-rbac --name "WorkshopVMDeploySP" --role "Contributor" --scopes "/subscriptions/<YOUR_SUBSCRIPTION_ID>"

Save the output (tenant ID, client ID, client secret) — we'll use these in our .NET app.

Step 2: Write the PowerShell Deployment Script

Create Deploy-VM.ps1 to handle Azure authentication and template deployment:

param(
    [string]$SubscriptionId,
    [string]$TenantId,
    [string]$ClientId,
    [string]$ClientSecret,
    [string]$ResourceGroupName,
    [string]$VmName,
    [string]$AdminPassword,
    [string]$Location = "eastus"
)

# Authenticate with the service principal
$secureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($ClientId, $secureSecret)
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $TenantId -Subscription $SubscriptionId

# Create resource group if it doesn't exist
if (-not (Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) {
    New-AzResourceGroup -Name $ResourceGroupName -Location $Location
}

# Deploy the ARM template
New-AzResourceGroupDeployment `
    -ResourceGroupName $ResourceGroupName `
    -TemplateFile "./vm-template.json" `
    -vmName $VmName `
    -adminPassword $AdminPassword `
    -Location $Location

Write-Output "✅ VM deployment initiated successfully for $VmName"

Step 3: Build the .NET Core Web API

Create a new .NET Core Web API project, then add a controller to handle deployment requests:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class VmDeploymentController : ControllerBase
{
    private readonly IConfiguration _config;
    private readonly PowerShellExecutor _psExecutor;

    public VmDeploymentController(IConfiguration config)
    {
        _config = config;
        _psExecutor = new PowerShellExecutor();
    }

    [HttpPost("deploy")]
    public async Task<IActionResult> DeployVm([FromBody] VmDeploymentRequest request)
    {
        try
        {
            // Pull Azure credentials from config (use environment variables in production!)
            var subscriptionId = _config["Azure:SubscriptionId"];
            var tenantId = _config["Azure:TenantId"];
            var clientId = _config["Azure:ClientId"];
            var clientSecret = _config["Azure:ClientSecret"];

            // Prepare script arguments
            var args = new[]
            {
                subscriptionId, tenantId, clientId, clientSecret,
                request.ResourceGroupName, request.VmName,
                request.AdminPassword, request.Location
            };

            // Run the deployment script
            var result = await _psExecutor.RunScriptAsync("./Deploy-VM.ps1", args);

            return Ok(new { Status = "Success", Details = result });
        }
        catch (Exception ex)
        {
            return BadRequest(new { Status = "Failed", Error = ex.Message });
        }
    }
}

// Request model to accept frontend input
public class VmDeploymentRequest
{
    public string ResourceGroupName { get; set; }
    public string VmName { get; set; }
    public string AdminPassword { get; set; }
    public string Location { get; set; } = "eastus";
}

Add Azure credentials to appsettings.json (replace placeholders with your service principal details):

{
  "Azure": {
    "SubscriptionId": "YOUR_SUBSCRIPTION_ID",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET"
  }
}

Step 4: Create a Simple Frontend

Build a basic HTML page (index.html) for participants to interact with:

<!DOCTYPE html>
<html>
<head>
    <title>Workshop VM Deployer</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 600px; margin: 2rem auto; padding: 0 1rem; }
        .form-group { margin-bottom: 1rem; }
        button { padding: 0.5rem 1rem; background: #0078d4; color: white; border: none; border-radius: 4px; cursor: pointer; }
        #result { margin-top: 1rem; padding: 1rem; border-radius: 4px; }
        .success { background: #d4edda; color: #155724; }
        .error { background: #f8d7da; color: #721c24; }
    </style>
</head>
<body>
    <h1>Deploy Your Workshop VM</h1>
    <div class="form-group">
        <label>Resource Group Name:</label>
        <input type="text" id="rgName" value="WorkshopRG">
    </div>
    <div class="form-group">
        <label>VM Name:</label>
        <input type="text" id="vmName" value="MyWorkshopVM">
    </div>
    <div class="form-group">
        <label>Admin Password:</label>
        <input type="password" id="adminPassword" placeholder="Enter a strong password">
    </div>
    <div class="form-group">
        <label>Location:</label>
        <input type="text" id="location" value="eastus">
    </div>
    <button onclick="deployVm()">Deploy VM</button>
    <div id="result"></div>

    <script>
        async function deployVm() {
            const resultDiv = document.getElementById('result');
            resultDiv.textContent = "Starting deployment...";
            resultDiv.className = "";

            const requestData = {
                resourceGroupName: document.getElementById('rgName').value,
                vmName: document.getElementById('vmName').value,
                adminPassword: document.getElementById('adminPassword').value,
                location: document.getElementById('location').value
            };

            try {
                const response = await fetch('/api/vmdeployment/deploy', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(requestData)
                });

                const data = await response.json();

                if (response.ok) {
                    resultDiv.innerHTML = `<strong>Success!</strong><br>${data.Details}`;
                    resultDiv.className = "success";
                } else {
                    resultDiv.innerHTML = `<strong>Error!</strong><br>${data.Error}`;
                    resultDiv.className = "error";
                }
            } catch (ex) {
                resultDiv.innerHTML = `<strong>Error!</strong><br>Failed to connect to API: ${ex.message}`;
                resultDiv.className = "error";
            }
        }
    </script>
</body>
</html>

Step 5: Workshop Setup Tips

  • Pre-Requisites: Ask participants to install .NET Core SDK, Azure PowerShell module, and have an Azure subscription.
  • Hands-On Tasks: Let participants modify the ARM template (e.g., switch to a Linux image, change VM size) or add more input fields to the frontend.
  • Cleanup Script: Provide a quick script to delete resources after the Workshop:
    param([string]$ResourceGroupName)
    Remove-AzResourceGroup -Name $ResourceGroupName -Force -Confirm:$false
    

内容的提问来源于stack exchange,提问作者MBob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 16:47:50