public async Task CallHttpActionOrchestrationWithManagedIdentity() { DurableHttpRequest request = null; // Mock the CallHttpAsync API so we can capture the request and return a fixed response. var contextMock = new Mock <IDurableOrchestrationContext>(); contextMock .Setup(ctx => ctx.CallHttpAsync(It.IsAny <DurableHttpRequest>())) .Callback <DurableHttpRequest>(req => request = req) .Returns(Task.FromResult(new DurableHttpResponse(System.Net.HttpStatusCode.OK))); var shim = new OutOfProcOrchestrationShim(contextMock.Object); var executionJson = @" { ""isDone"": false, ""actions"": [ [{ ""actionType"": ""CallHttp"", ""httpRequest"": { ""method"": ""GET"", ""uri"": ""https://example.com"", ""tokenSource"": { ""kind"": ""AzureManagedIdentity"", ""resource"": ""https://management.core.windows.net/.default"" } } }] ] }"; // Feed the out-of-proc execution result JSON to the out-of-proc shim. var jsonObject = JObject.Parse(executionJson); OrchestrationInvocationResult result = new OrchestrationInvocationResult() { ReturnValue = jsonObject, }; bool moreWork = await shim.ScheduleDurableTaskEvents(result); Assert.NotNull(request); Assert.Equal(HttpMethod.Get, request.Method); Assert.Equal(new Uri("https://example.com"), request.Uri); Assert.Null(request.Content); Assert.NotNull(request.TokenSource); ManagedIdentityTokenSource tokenSource = Assert.IsType <ManagedIdentityTokenSource>(request.TokenSource); Assert.Equal("https://management.core.windows.net/.default", tokenSource.Resource); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { ResourceInfo vmInfo = context.GetInput <ResourceInfo>(); string apiVersion = vmInfo.ApiVersion ?? "2018-06-01"; string subscriptionId = vmInfo.SubscriptionId; string resourceGroup = vmInfo.ResourceGroup; // Implicitly uses the Azure AD identity of the current app to make an HTTP call to Azure Resource Manager var managedIdentity = new ManagedIdentityTokenSource("https://management.core.windows.net"); // List all of the VMs in my subscription and add them to a list. DurableHttpRequest request = new DurableHttpRequest( HttpMethod.Get, new Uri($"https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/virtualMachines?api-version=2018-06-01"), tokenSource: managedIdentity); DurableHttpResponse response = await context.CallHttpAsync(request); if (response.StatusCode != HttpStatusCode.OK) { throw new ArgumentException($"Failed to list VMs: {response.StatusCode}: {response.Content}"); } // Deserializes content to just get the names of the VMs in the subscription JObject jObject = JsonConvert.DeserializeObject <JObject>(response.Content); var vmNamesList = new List <string>(); foreach (JToken value in jObject["value"]) { string vmName = value["name"].ToString(); vmNamesList.Add(vmName); } // Restart all of the VMs in my subscription foreach (string vmName in vmNamesList) { var restartRequest = new DurableHttpRequest( HttpMethod.Post, new Uri($"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{vmName}/restart?api-version={apiVersion}"), tokenSource: managedIdentity); DurableHttpResponse restartResponse = await context.CallHttpAsync(restartRequest); if (restartResponse.StatusCode != HttpStatusCode.OK) { throw new ArgumentException($"Failed to restart VM: {restartResponse.StatusCode}: {restartResponse.Content}"); } } }
public async Task OpenCircuit( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { if (context == null) { throw new ArgumentNullException(nameof(context)); } try { var failureRequest = context.GetInput <FailureRequest>(); var resourceId = failureRequest.ResourceId; if (!context.IsReplaying) { log.LogInformation("Disabling function app ({resourceId}) to open circuit.", resourceId); } var managedIdentity = new ManagedIdentityTokenSource("https://management.azure.com/.default"); // Note: Configurable resourceId works only for other Azure App Service applications. var stopFunctionRequest = new DurableHttpRequest( HttpMethod.Post, new Uri($"https://management.azure.com{resourceId}/stop?api-version=2019-08-01"), tokenSource: managedIdentity); // Note: Use of ConfigureAwait can break orchestration tasks (prevents from completing; instance remains in a 'running' state). // See https://github.com/Azure/azure-functions-durable-extension/issues/995 and // https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-code-constraints #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task DurableHttpResponse restartResponse = await context.CallHttpAsync(stopFunctionRequest); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task if (restartResponse.StatusCode != HttpStatusCode.OK) { if (!context.IsReplaying) { log.LogError("Failed to stop Function App: {restartResponse.StatusCode}: {restartResponse.Content}", restartResponse.StatusCode, restartResponse.Content); } throw new ApplicationException($"Failed to stop Function App: {restartResponse.StatusCode}: {restartResponse.Content}"); } if (restartResponse.StatusCode == HttpStatusCode.OK) { if (!context.IsReplaying) { // Logging as a warning to aide in noticing this messages, as stopping of an Azure Function // may be an action worthy of human intervention. log.LogWarning("Successfully STOPPED Azure Function with Resource ID {resourceId}.", resourceId); } } return; } catch (Exception e) { log.LogError(e, "Unexpected error '{msg}' while processing.", e.Message); throw; } }