user2781163
user2781163

Reputation: 169

Azure Function role like permissions to Stop Azure Virtual Machines

I'm hoping to manage some Azure resources using a scheduled C# Azure Function.

Currently in a command line application I've made, I've been using libraries 'Microsoft.IdentityModel.Clients.ActiveDirectory' for token authorization and 'Microsoft.Azure.Management.Compute' for client calls for resource management like so.

//... var credential generated my AD authentication and extending Microsoft.Rest.ServiceClientCredentials
using (var client = new ComputeManagementClient(credential)) {
    client.SubscriptionId = "[SOME_SUBSCRIPTION_ID]";
    client.VirtualMachines.BeginPowerOff("[RESOURCE_GROUP]", "[VM_NAME]");
}

Can my management client interact with Azure resources without providing a User Credential or Key-Secret like credential establishment?

My previous experience is related to AWS and admittedly it has confused my view of Azure Resource Management.

Older posts I've looked at are: Start and Stop Azure Virtual Machine

and

Is it possible to stop/start an Azure ARM Virtual from an Azure Function?

-EDIT 1-

I was hoping for something similar to run-time credentials in AWS resource clients for Lambda based on an assigned role with a variety of permissions. I will have a look at certificates though.

Upvotes: 2

Views: 6899

Answers (2)

Ling Toh
Ling Toh

Reputation: 2474

There are a few resources online on using C# to make REST API calls to start and stop a VM. Here's a link to such a document:

https://msftstack.wordpress.com/2016/01/03/how-to-call-the-azure-resource-manager-rest-api-from-c/

You could use the above as a reference to create C# Functions to start/stop your VM.

However, using C# to make these REST calls requires pre-packaging the HTTP request and post processing the HTTP response. If your use-case just calls for a start/stop VM, an easier approach would be use PowerShell in Azure Functions to call the Start-AzureRmVM and Stop-AzureRmVM cmdlets.

The following are steps on how to create HTTP-triggered PowerShell Functions to start and stop a VM:

  1. Setup a service principal to obtain the username, password and tenant id. This initial setup may be considered tedious by some users, but since it's a one-time task, I feel that it is worth it to leverage running Azure PowerShell in Functions. There are many docs online, but here are some links to documents on how to setup your service principal:

    i. http://blog.davidebbo.com/2014/12/azure-service-principal.html (I used this one)

    ii. https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal

  2. Log into the Functions portal to access your Function app.

  3. Click on Function app settings->Configure app settings and add the key-value pairs for the settings SP_USERNAME, SP_PASSWORD, and TENANTID (You may use other desired key names).

  4. Create an HTTP-triggered PowerShell Function named, e.g. StartVm with the following content in its run.ps1 file.

    $requestBody = Get-Content $req -Raw | ConvertFrom-Json

    # Set Service Principal credentials
    # SP_PASSWORD, SP_USERNAME, TENANTID are app settings
    $secpasswd = ConvertTo-SecureString $env:SP_PASSWORD -AsPlainText -Force;
    $mycreds = New-Object System.Management.Automation.PSCredential ($env:SP_USERNAME, $secpasswd)
    Add-AzureRmAccount -ServicePrincipal -Tenant $env:TENANTID -Credential $mycreds;
    $context = Get-AzureRmContext;
    Set-AzureRmContext -Context $context;

    # Start VM
    Start-AzureRmVM -ResourceGroupName $requestBody.resourcegroup -Name  $requestBody.vmname | Out-String

  1. Click on the Save button.

  2. Next, click on the Logs button to open the log viewer.

  3. Click on the Test button to open the simple HTTP client. In the request body, provide the vmname and resourcegroup values for the VM, e.g.

    {
        "vmname": "testvm",
        "resourcegroup": "testresourcegroup"
    }
  1. Click on the Run button and wait for a few seconds. It takes some time for the Start-AzureRmVM cmdlet to run to completion. When it does, you should see similar entries in the log viewer.

    2016-11-30T07:11:26.479 Function started (Id=1e38ae2c-3cca-4e2f-a85d-f62c0d565c34)
    2016-11-30T07:11:28.276 Microsoft.Azure.Commands.Profile.Models.PSAzureContext
    2016-11-30T07:11:28.276 Microsoft.Azure.Commands.Profile.Models.PSAzureContext
    2016-11-30T07:11:59.312 RequestId            IsSuccessStatusCode          StatusCode ReasonPhrase       
    ---------            -------------------          ---------- ------------       
                                        True                  OK OK
    2016-11-30T07:11:59.327 Function completed (Success, Id=1e38ae2c-3cca-4e2f-a85d-f62c0d565c34)

  1. Repeat steps 4-8 to create the StopVm Function with the following content in its run.ps1 file. If the execution succeeds, the log output should be similar to the log entries for the StartVm Function.

    $requestBody = Get-Content $req -Raw | ConvertFrom-Json

    # Set Service Principal credentials
    # SP_PASSWORD, SP_USERNAME, TENANTID are app settings
    $secpasswd = ConvertTo-SecureString $env:SP_PASSWORD -AsPlainText -Force;
    $mycreds = New-Object System.Management.Automation.PSCredential ($env:SP_USERNAME, $secpasswd)
    Add-AzureRmAccount -ServicePrincipal -Tenant $env:TENANTID -Credential $mycreds;
    $context = Get-AzureRmContext;
    Set-AzureRmContext -Context $context;

    # Stop VM
    Stop-AzureRmVM -ResourceGroupName $requestBody.resourcegroup -Name $requestBody.vmname -Force | Out-String

  1. When the StopVm Function execution succeeds, you may also add another GetVm Function with the following content in its run.ps1 file to verify that the VM has indeed been stopped.

    $requestBody = Get-Content $req -Raw | ConvertFrom-Json

    # Set Service Principal credentials
    # SP_PASSWORD, SP_USERNAME, TENANTID are app settings
    $secpasswd = ConvertTo-SecureString $env:SP_PASSWORD -AsPlainText -Force;
    $mycreds = New-Object System.Management.Automation.PSCredential ($env:SP_USERNAME, $secpasswd)
    Add-AzureRmAccount -ServicePrincipal -Tenant $env:TENANTID -Credential $mycreds;
    $context = Get-AzureRmContext;
    Set-AzureRmContext -Context $context;

    # Get VM
    Get-AzureRmVM -ResourceGroupName $requestBody.resourcegroup -Name $requestBody.vmname -Status | Out-String

The log entries for the GetVM Function on a stopped VM will would be similar to the following:


    2016-11-30T07:53:59.956 Function started (Id=1841757f-bbb8-45cb-8777-80edb4e75ced)
    2016-11-30T07:54:02.040 Microsoft.Azure.Commands.Profile.Models.PSAzureContext
    2016-11-30T07:54:02.040 Microsoft.Azure.Commands.Profile.Models.PSAzureContext
    2016-11-30T07:54:02.977 ResourceGroupName          : testresourcegroup
    Name                       : testvm
    BootDiagnostics            : 
      ConsoleScreenshotBlobUri : https://teststorage.blob.core.windows.net/boot
    diagnostics-vmtest-[someguid]/testvm.[someguid].screenshot.bmp
    Disks[0]                   : 
      Name                     : windowsvmosdisk
      Statuses[0]              : 
        Code                   : ProvisioningState/succeeded
        Level                  : Info
        DisplayStatus          : Provisioning succeeded
        Time                   : 11/30/2016 7:15:15 AM
    Extensions[0]              : 
      Name                     : BGInfo
    VMAgent                    : 
      VmAgentVersion           : Unknown
      Statuses[0]              : 
        Code                   : ProvisioningState/Unavailable
        Level                  : Warning
        DisplayStatus          : Not Ready
        Message                : VM Agent is unresponsive.
        Time                   : 11/30/2016 7:54:02 AM
    Statuses[0]                : 
      Code                     : ProvisioningState/succeeded
      Level                    : Info
      DisplayStatus            : Provisioning succeeded
      Time                     : 11/30/2016 7:15:15 AM
    Statuses[1]                : 
      Code                     : PowerState/deallocated
      Level                    : Info
      DisplayStatus            : VM deallocated
    2016-11-30T07:54:02.977 Function completed (Success, Id=1841757f-bbb8-45cb-8777-80edb4e75ced)

Note: FYI, while you may write a Function to create a VM by calling the New-AzureRmVM cmdlet, it will not run to completion in Azure Functions. VM creation in Azure Function's infrastructure seem to take ~9 mins to complete but a Function's execution is terminated at 5 minutes. You may write another script to poll the results separately. This limitation will be lifted when we start supporting custom configuration for maximum execution time in one of our upcoming releases.

--Update-- I just realized you were trying to create scheduled Functions. In that case, you can use Timer-triggered PowerShell Functions and hard-code the vmname and resourcegroup.

Upvotes: 9

4c74356b41
4c74356b41

Reputation: 72171

Well, I don't really understand how do you expect to authenticate without authenticating, I guess your only option would be certificates?
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-daemon-certificate-credential/

Upvotes: 0

Related Questions