diff --git a/source/Calamari.Azure/AppServices/AzureTargetSite.cs b/source/Calamari.Azure/AppServices/AzureTargetSite.cs
index 5888e1c88..4900b75fd 100644
--- a/source/Calamari.Azure/AppServices/AzureTargetSite.cs
+++ b/source/Calamari.Azure/AppServices/AzureTargetSite.cs
@@ -5,7 +5,6 @@
using Calamari.CloudAccounts;
using Calamari.Common.Plumbing.Logging;
using Calamari.Common.Plumbing.Variables;
-using Microsoft.Azure.Management.Fluent;
namespace Calamari.Azure.AppServices
{
diff --git a/source/Calamari.Azure/AzureClient.cs b/source/Calamari.Azure/AzureClient.cs
index 1be95c9a8..9e211bcc9 100644
--- a/source/Calamari.Azure/AzureClient.cs
+++ b/source/Calamari.Azure/AzureClient.cs
@@ -21,9 +21,8 @@ public static class AzureClient
///
public static ArmClient CreateArmClient(this IAzureAccount azureAccount, Action retryOptionsSetter = null)
{
- if (azureAccount.AccountType == AccountType.AzureOidc)
+ if (azureAccount is AzureOidcAccount oidcAccount)
{
- var oidcAccount = (AzureOidcAccount)azureAccount;
var (clientOptions, _) = GetArmClientOptions(azureAccount, retryOptionsSetter);
var clientAssertionCreds = new ClientAssertionCredential(oidcAccount.TenantId, oidcAccount.ClientId, () => oidcAccount.GetCredentials);
return new ArmClient(clientAssertionCreds, defaultSubscriptionId: azureAccount.SubscriptionNumber, clientOptions);
@@ -76,22 +75,5 @@ public static (ArmClientOptions, TokenCredentialOptions) GetArmClientOptions(thi
return (armClientOptions, tokenCredentialOptions);
}
-
- public static async Task GetAccessTokenAsync(this IAzureAccount azureAccount)
- {
- return azureAccount.AccountType == AccountType.AzureOidc
- ? await AzureOidcAccountExtensions.GetAuthorizationToken(azureAccount.TenantId,
- azureAccount.ClientId,
- azureAccount.GetCredentials,
- azureAccount.ResourceManagementEndpointBaseUri,
- azureAccount.ActiveDirectoryEndpointBaseUri,
- azureAccount.AzureEnvironment,
- CancellationToken.None)
- : await AzureServicePrincipalAccountExtensions.GetAuthorizationToken(azureAccount.TenantId,
- azureAccount.ClientId,
- azureAccount.GetCredentials,
- azureAccount.ResourceManagementEndpointBaseUri,
- azureAccount.ActiveDirectoryEndpointBaseUri);
- }
}
}
\ No newline at end of file
diff --git a/source/Calamari.Azure/AzureKnownEnvironment.cs b/source/Calamari.Azure/AzureKnownEnvironment.cs
index 21ef40666..2424eacf5 100644
--- a/source/Calamari.Azure/AzureKnownEnvironment.cs
+++ b/source/Calamari.Azure/AzureKnownEnvironment.cs
@@ -1,59 +1,46 @@
using System;
using Azure.Identity;
using Azure.ResourceManager;
-using Microsoft.Azure.Management.ResourceManager.Fluent;
namespace Calamari.Azure
{
public sealed class AzureKnownEnvironment
- {
+ {
/// The environment name exactly matching the names defined in Azure SDK (see here https://github.com/Azure/azure-libraries-for-net/blob/master/src/ResourceManagement/ResourceManager/AzureEnvironment.cs)
/// Other names are allowed in case this list is ever expanded/changed, but will likely result in an error at deployment time.
///
public AzureKnownEnvironment(string environment)
{
Value = environment;
-
- if (string.IsNullOrEmpty(environment) || environment == "AzureCloud") // This environment name is defined in Sashimi.Azure.Accounts.AzureEnvironmentsListAction
- Value = Global.Value; // We interpret it as the normal Azure environment for historical reasons)
- azureSdkEnvironment = AzureEnvironment.FromName(Value) ??
- throw new InvalidOperationException($"Unknown environment name {Value}");
+ if (string.IsNullOrEmpty(environment) || environment == "AzureCloud") // This environment name is defined in Sashimi.Azure.Accounts.AzureEnvironmentsListAction
+ {
+ Value = "AzureGlobalCloud"; // We interpret it as the normal Azure environment for historical reasons)
+ }
}
- private readonly AzureEnvironment azureSdkEnvironment;
public string Value { get; }
- public static readonly AzureKnownEnvironment Global = new AzureKnownEnvironment("AzureGlobalCloud");
- public static readonly AzureKnownEnvironment AzureChinaCloud = new AzureKnownEnvironment("AzureChinaCloud");
- public static readonly AzureKnownEnvironment AzureUSGovernment = new AzureKnownEnvironment("AzureUSGovernment");
- public static readonly AzureKnownEnvironment AzureGermanCloud = new AzureKnownEnvironment("AzureGermanCloud");
-
- public AzureEnvironment AsAzureSDKEnvironment()
- {
- return azureSdkEnvironment;
- }
-
public ArmEnvironment AsAzureArmEnvironment() => ToArmEnvironment(Value);
- private static ArmEnvironment ToArmEnvironment(string name) => name switch
- {
- "AzureGlobalCloud" => ArmEnvironment.AzurePublicCloud,
- "AzureChinaCloud" => ArmEnvironment.AzureChina,
- "AzureGermanCloud" => ArmEnvironment.AzureGermany,
- "AzureUSGovernment" => ArmEnvironment.AzureGovernment,
- _ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
- };
-
+ static ArmEnvironment ToArmEnvironment(string name) => name switch
+ {
+ "AzureGlobalCloud" => ArmEnvironment.AzurePublicCloud,
+ "AzureChinaCloud" => ArmEnvironment.AzureChina,
+ "AzureGermanCloud" => ArmEnvironment.AzureGermany,
+ "AzureUSGovernment" => ArmEnvironment.AzureGovernment,
+ _ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
+ };
+
public Uri GetAzureAuthorityHost() => ToAzureAuthorityHost(Value);
- private static Uri ToAzureAuthorityHost(string name) => name switch
- {
- "AzureGlobalCloud" => AzureAuthorityHosts.AzurePublicCloud,
- "AzureChinaCloud" => AzureAuthorityHosts.AzureChina,
- "AzureGermanCloud" => AzureAuthorityHosts.AzureGermany,
- "AzureUSGovernment" => AzureAuthorityHosts.AzureGovernment,
- _ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
- };
+ static Uri ToAzureAuthorityHost(string name) => name switch
+ {
+ "AzureGlobalCloud" => AzureAuthorityHosts.AzurePublicCloud,
+ "AzureChinaCloud" => AzureAuthorityHosts.AzureChina,
+ "AzureGermanCloud" => AzureAuthorityHosts.AzureGermany,
+ "AzureUSGovernment" => AzureAuthorityHosts.AzureGovernment,
+ _ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
+ };
}
-}
+}
\ No newline at end of file
diff --git a/source/Calamari.Azure/Calamari.Azure.csproj b/source/Calamari.Azure/Calamari.Azure.csproj
index e2b136310..b27774923 100644
--- a/source/Calamari.Azure/Calamari.Azure.csproj
+++ b/source/Calamari.Azure/Calamari.Azure.csproj
@@ -10,9 +10,10 @@
net462;net6.0
-
-
+
+
+
diff --git a/source/Calamari.Azure/Kubernetes/Discovery/AzureKubernetesDiscoverer.cs b/source/Calamari.Azure/Kubernetes/Discovery/AzureKubernetesDiscoverer.cs
index aba6f63ae..8f68657b7 100644
--- a/source/Calamari.Azure/Kubernetes/Discovery/AzureKubernetesDiscoverer.cs
+++ b/source/Calamari.Azure/Kubernetes/Discovery/AzureKubernetesDiscoverer.cs
@@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
+using Azure;
+using Azure.ResourceManager.ContainerService;
+using Azure.ResourceManager.Resources;
using Calamari.CloudAccounts;
using Calamari.Common.Features.Discovery;
using Calamari.Common.Plumbing.Logging;
-using Microsoft.Rest.Azure;
using Newtonsoft.Json;
namespace Calamari.Azure.Kubernetes.Discovery
@@ -39,38 +41,41 @@ public override IEnumerable DiscoverClusters(string contextJs
Log.Verbose($" Subscription ID: {account.SubscriptionNumber}");
Log.Verbose($" Tenant ID: {account.TenantId}");
Log.Verbose($" Client ID: {account.ClientId}");
- var azureClient = account.CreateAzureClient();
- var discoveredClusters = new List();
+ var armClient = account.CreateArmClient();
- // There appears to be an issue where the azure client returns stale data
- // We need to upgrade this to use the newer SDK, but we need to upgrade to .NET 4.6.2 to support that.
- var resourceGroups = azureClient.ResourceGroups.List();
- //we don't care about resource groups that are being deleted
- foreach (var resourceGroup in resourceGroups.Where(rg => rg.ProvisioningState != "Deleting"))
+ var discoveredClusters = new List();
+
+ var subscriptionResource = armClient.GetSubscriptionResource(SubscriptionResource.CreateResourceIdentifier(account.SubscriptionNumber));
+
+ var resourceGroups = subscriptionResource.GetResourceGroups().GetAll();//.GetAll("provisioningState ne 'Deleting'");
+
+ //we don't care about resource groups that are being deleted
+ foreach (var resourceGroupResource in resourceGroups.Where(rg => !string.Equals(rg.Data.ResourceGroupProvisioningState,"Deleting", StringComparison.OrdinalIgnoreCase)))
{
try
{
// There appears to be an issue where the azure client returns stale data
// to mitigate this, specifically for scenario's where the resource group doesn't exist anymore
// we specifically list the clusters in each resource group
- var clusters = azureClient.KubernetesClusters.ListByResourceGroup(resourceGroup.Name);
+ var clusters = resourceGroupResource.GetContainerServiceManagedClusters().GetAll();
+
discoveredClusters.AddRange(
clusters
.Select(c => KubernetesCluster.CreateForAks(
- $"aks/{account.SubscriptionNumber}/{c.ResourceGroupName}/{c.Name}",
- c.Name,
- c.ResourceGroupName,
+ $"aks/{account.SubscriptionNumber}/{resourceGroupResource.Data.Name}/{c.Data.Name}",
+ c.Data.Name,
+ resourceGroupResource.Data.Name,
accountId,
- c.Tags.ToTargetTags())));
+ c.Data.Tags.ToTargetTags())));
}
- catch (CloudException ex)
+ catch (RequestFailedException ex)
{
- Log.Verbose($"Failed to list kubernetes clusters for resource group {resourceGroup.Name}. Response message: {ex.Message}, Status code: {ex.Response.StatusCode}");
+ Log.Verbose($"Failed to list kubernetes clusters for resource group {resourceGroupResource.Data.Name}. Response message: {ex.Message}, Status code: {ex.Status}");
// if the resource group was not found, we don't care and move on
- if (ex.Response.StatusCode == HttpStatusCode.NotFound && ex.Message.StartsWith("Resource group"))
+ if (ex.Status == (int)HttpStatusCode.NotFound && ex.GetRawResponse()?.Content.ToString().StartsWith("Resource group") == true)
continue;
//throw in all other scenario's
diff --git a/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj b/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj
index f861374e2..e6e9763ec 100644
--- a/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj
+++ b/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj
@@ -10,15 +10,13 @@
-
-
+
-
diff --git a/source/Calamari.AzureAppService/Behaviors/AzureAppServiceZipDeployBehaviour.cs b/source/Calamari.AzureAppService/Behaviors/AzureAppServiceZipDeployBehaviour.cs
index f9c6b8c39..af81055fd 100644
--- a/source/Calamari.AzureAppService/Behaviors/AzureAppServiceZipDeployBehaviour.cs
+++ b/source/Calamari.AzureAppService/Behaviors/AzureAppServiceZipDeployBehaviour.cs
@@ -132,6 +132,7 @@ public async Task Execute(RunningDeployment context)
{
uploadFile = await packageProvider.ConvertToAzureSupportedFile(packageFileInfo);
}
+
uploadPath = uploadFile.FullName;
uploadFileNeedsCleaning = packageFileInfo.Extension != uploadFile.Extension;
@@ -156,14 +157,26 @@ public async Task Execute(RunningDeployment context)
//Need to check if site turn off
var scmPublishEnabled = await armClient.IsScmPublishEnabled(targetSite);
-
+
if (packageProvider.SupportsAsynchronousDeployment && FeatureToggle.AsynchronousAzureZipDeployFeatureToggle.IsEnabled(context.Variables))
{
- await UploadZipAndPollAsync(account, publishingProfile, scmPublishEnabled, uploadPath, targetSite.ScmSiteAndSlot, packageProvider, pollingTimeout, asyncZipDeployTimeoutPolicy);
+ await UploadZipAndPollAsync(account,
+ publishingProfile,
+ scmPublishEnabled,
+ uploadPath,
+ targetSite.ScmSiteAndSlot,
+ packageProvider,
+ pollingTimeout,
+ asyncZipDeployTimeoutPolicy);
}
else
{
- await UploadZipAsync(account, publishingProfile, scmPublishEnabled, uploadPath, targetSite.ScmSiteAndSlot, packageProvider);
+ await UploadZipAsync(account,
+ publishingProfile,
+ scmPublishEnabled,
+ uploadPath,
+ targetSite.ScmSiteAndSlot,
+ packageProvider);
}
}
finally
@@ -237,7 +250,7 @@ private async Task UploadZipAsync(IAzureAccount azureAccount,
#endif
using var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
-
+
//we have to create a new request message each time
var request = new HttpRequestMessage(HttpMethod.Post, zipUploadUrl)
{
@@ -259,7 +272,7 @@ private async Task UploadZipAsync(IAzureAccount azureAccount,
Log.Verbose("Finished deploying");
}
- static async Task GetAuthenticationHeaderValue(IAzureAccount azureAccount, PublishingProfile publishingProfile, bool scmPublishEnabled)
+ async Task GetAuthenticationHeaderValue(IAzureAccount azureAccount, PublishingProfile publishingProfile, bool scmPublishEnabled)
{
AuthenticationHeaderValue authenticationHeader;
if (scmPublishEnabled)
@@ -268,7 +281,7 @@ static async Task GetAuthenticationHeaderValue(IAzure
}
else
{
- var accessToken = await azureAccount.GetAccessTokenAsync();
+ var accessToken = await azureAccount.GetAuthorizationToken(Log, CancellationToken.None);
authenticationHeader = new AuthenticationHeaderValue("Bearer", accessToken);
}
diff --git a/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj b/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj
index 9840d4e4c..605223238 100644
--- a/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj
+++ b/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj
@@ -23,7 +23,6 @@
-
diff --git a/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj b/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj
index 72bbee5c2..bd942d08a 100644
--- a/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj
+++ b/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj
@@ -12,11 +12,9 @@
-
-
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/source/Calamari.AzureResourceGroup/LegacyDeployAzureResourceGroupBehaviour.cs b/source/Calamari.AzureResourceGroup/LegacyDeployAzureResourceGroupBehaviour.cs
index aab959b9f..7ea9e7829 100644
--- a/source/Calamari.AzureResourceGroup/LegacyDeployAzureResourceGroupBehaviour.cs
+++ b/source/Calamari.AzureResourceGroup/LegacyDeployAzureResourceGroupBehaviour.cs
@@ -12,9 +12,9 @@
using Calamari.Common.Plumbing.Variables;
using Microsoft.Azure.Management.ResourceManager;
using Microsoft.Azure.Management.ResourceManager.Models;
+using Microsoft.Rest;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-using Octopus.CoreUtilities.Extensions;
using AzureResourceManagerDeployment = Microsoft.Azure.Management.ResourceManager.Models.Deployment;
namespace Calamari.AzureResourceGroup
@@ -31,9 +31,9 @@ public LegacyDeployAzureResourceGroupBehaviour(TemplateService templateService,
this.parameterNormalizer = parameterNormalizer;
this.log = log;
}
-
+
public bool IsEnabled(RunningDeployment context) => !FeatureToggle.ModernAzureSdkFeatureToggle.IsEnabled(context.Variables);
-
+
public async Task Execute(RunningDeployment deployment)
{
var variables = deployment.Variables;
@@ -49,6 +49,7 @@ public async Task Execute(RunningDeployment deployment)
templateFile = variables.Get(SpecialVariables.Action.Azure.ResourceGroupTemplate);
templateParametersFile = variables.Get(SpecialVariables.Action.Azure.ResourceGroupTemplateParameters);
}
+
var resourceManagementEndpoint = variables.Get(AzureAccountVariables.ResourceManagementEndPoint, DefaultVariables.ResourceManagementEndpoint);
if (resourceManagementEndpoint != DefaultVariables.ResourceManagementEndpoint)
@@ -62,7 +63,7 @@ public async Task Execute(RunningDeployment deployment)
? variables[SpecialVariables.Action.Azure.ResourceGroupDeploymentName]
: GenerateDeploymentNameFromStepName(variables[ActionVariables.Name]);
var deploymentMode = (DeploymentMode)Enum.Parse(typeof(DeploymentMode),
- variables[SpecialVariables.Action.Azure.ResourceGroupDeploymentMode]);
+ variables[SpecialVariables.Action.Azure.ResourceGroupDeploymentMode]);
var template = templateService.GetSubstitutedTemplateContent(templateFile, filesInPackageOrRepository, variables);
var parameters = !string.IsNullOrWhiteSpace(templateParametersFile)
? parameterNormalizer.Normalize(templateService.GetSubstitutedTemplateContent(templateParametersFile, filesInPackageOrRepository, variables))
@@ -72,21 +73,25 @@ public async Task Execute(RunningDeployment deployment)
// We re-create the client each time it is required in order to get a new authorization-token. Else, the token can expire during long-running deployments.
Func> createArmClient = async () =>
- {
- var token = !jwt.IsNullOrEmpty()
- ? await new AzureOidcAccount(variables).Credentials(CancellationToken.None)
- : await new AzureServicePrincipalAccount(variables).Credentials();
- var resourcesClient = new ResourceManagementClient(token, AuthHttpClientFactory.ProxyClientHandler())
- {
- SubscriptionId = subscriptionId,
- BaseUri = new Uri(resourceManagementEndpoint),
- };
- resourcesClient.HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
- resourcesClient.HttpClient.BaseAddress = new Uri(resourceManagementEndpoint);
- return resourcesClient;
- };
-
- await CreateDeployment(createArmClient, resourceGroupName, deploymentName, deploymentMode, template, parameters);
+ {
+ var account = AzureAccountFactory.Create(variables);
+ var token = new TokenCredentials(await account.GetAuthorizationToken(log, CancellationToken.None));
+ var resourcesClient = new ResourceManagementClient(token, AuthHttpClientFactory.ProxyClientHandler())
+ {
+ SubscriptionId = subscriptionId,
+ BaseUri = new Uri(resourceManagementEndpoint),
+ };
+ resourcesClient.HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
+ resourcesClient.HttpClient.BaseAddress = new Uri(resourceManagementEndpoint);
+ return resourcesClient;
+ };
+
+ await CreateDeployment(createArmClient,
+ resourceGroupName,
+ deploymentName,
+ deploymentMode,
+ template,
+ parameters);
await PollForCompletion(createArmClient, resourceGroupName, deploymentName, variables);
}
@@ -104,8 +109,12 @@ internal static string GenerateDeploymentNameFromStepName(string stepName)
return deploymentName;
}
- async Task CreateDeployment(Func> createArmClient, string resourceGroupName, string deploymentName,
- DeploymentMode deploymentMode, string template, string parameters)
+ async Task CreateDeployment(Func> createArmClient,
+ string resourceGroupName,
+ string deploymentName,
+ DeploymentMode deploymentMode,
+ string template,
+ string parameters)
{
log.Verbose($"Template:\n{template}\n");
if (parameters != null)
@@ -141,8 +150,10 @@ async Task CreateDeployment(Func> createArmClien
}
}
- async Task PollForCompletion(Func> createArmClient, string resourceGroupName,
- string deploymentName, IVariables variables)
+ async Task PollForCompletion(Func> createArmClient,
+ string resourceGroupName,
+ string deploymentName,
+ IVariables variables)
{
// While the deployment is running, we poll to check its state.
// We increase the poll interval according to the Fibonacci sequence, up to a maximum of 30 seconds.
@@ -250,14 +261,17 @@ void LogCloudError(Microsoft.Rest.Azure.CloudError error, int count)
{
log.Error($"{indent}Message: {error.Message}");
}
+
if (!string.IsNullOrEmpty(error.Code))
{
log.Error($"{indent}Code: {error.Code}");
}
+
if (!string.IsNullOrEmpty(error.Target))
{
log.Error($"{indent}Target: {error.Target}");
}
+
foreach (var errorDetail in error.Details)
{
LogCloudError(errorDetail, ++count);
diff --git a/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj b/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj
index deae6c7a7..28ac37fca 100644
--- a/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj
+++ b/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj
@@ -25,8 +25,4 @@
-
-
-
-
diff --git a/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj b/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj
index 2ac000069..084149b1d 100644
--- a/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj
+++ b/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj
@@ -10,9 +10,7 @@
8.0
-
-
-
+
+
diff --git a/source/Calamari.AzureWebApp.Tests/DeployAzureWebCommandFixture.cs b/source/Calamari.AzureWebApp.Tests/DeployAzureWebCommandFixture.cs
index 08c7a7c32..53c990aad 100644
--- a/source/Calamari.AzureWebApp.Tests/DeployAzureWebCommandFixture.cs
+++ b/source/Calamari.AzureWebApp.Tests/DeployAzureWebCommandFixture.cs
@@ -58,6 +58,8 @@ public async Task Setup()
tenantId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId, cancellationToken);
subscriptionId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionId, cancellationToken);
var resourceGroupLocation = Environment.GetEnvironmentVariable("AZURE_NEW_RESOURCE_REGION") ?? RandomAzureRegion.GetRandomRegionWithExclusions();
+
+ TestContext.WriteLine($"Resource Group Location: {resourceGroupLocation}");
var servicePrincipalAccount = new AzureServicePrincipalAccount(subscriptionId,
clientId,
@@ -110,11 +112,20 @@ public async Task Setup()
TestContext.WriteLine($"Creating app service plan {resourceGroupResource.Data.Name}");
- var servicePlanResponse = await resourceGroupResource.GetAppServicePlans()
+ ArmOperation servicePlanResponse;
+ try
+ {
+ servicePlanResponse = await resourceGroupResource.GetAppServicePlans()
.CreateOrUpdateAsync(WaitUntil.Completed,
resourceGroupResource.Data.Name,
appServicePlanData,
cancellationToken);
+ }
+ catch (RequestFailedException e)
+ {
+ //try and catch errors so we can filter out bad regions
+ throw new Exception($"Failed to create app service plan in resource group {resourceGroupResource.Data.Name} in region {resourceGroupLocation}.", e);
+ }
servicePlanResource = servicePlanResponse.Value;
}
diff --git a/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj b/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj
index 3bcc9e49c..19df55f98 100644
--- a/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj
+++ b/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj
@@ -11,11 +11,8 @@
-
-
-
diff --git a/source/Calamari.CloudAccounts/AzureOidcAccount.cs b/source/Calamari.CloudAccounts/AzureOidcAccount.cs
index 3d039604b..da77abc6f 100644
--- a/source/Calamari.CloudAccounts/AzureOidcAccount.cs
+++ b/source/Calamari.CloudAccounts/AzureOidcAccount.cs
@@ -1,15 +1,10 @@
using System;
-using System.Net;
-using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using Calamari.Common.Plumbing.Logging;
using Calamari.Common.Plumbing.Variables;
-using Microsoft.Azure.Management.Fluent;
-using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
-using Microsoft.Rest;
+using Microsoft.Identity.Client;
using Newtonsoft.Json;
-using NetWebRequest = System.Net.WebRequest;
-using AzureEnvironmentEnum = Microsoft.Azure.Management.ResourceManager.Fluent.AzureEnvironment;
namespace Calamari.CloudAccounts
{
@@ -41,13 +36,14 @@ public AzureOidcAccount(IVariables variables)
TenantId = variables.Get(AccountVariables.TenantId);
Jwt = variables.Get(AccountVariables.Jwt);
AzureEnvironment = variables.Get(AccountVariables.Environment);
- ResourceManagementEndpointBaseUri = variables.Get(AccountVariables.ResourceManagementEndPoint, DefaultVariables.ResourceManagementEndpoint);
- ActiveDirectoryEndpointBaseUri = variables.Get(AccountVariables.ActiveDirectoryEndPoint, DefaultVariables.OidcAuthContextUri);
+ ResourceManagementEndpointBaseUri = variables.Get(AccountVariables.ResourceManagementEndPoint, DefaultAccountEndpoints.ResourceManagementEndpoint);
+ ActiveDirectoryEndpointBaseUri = variables.Get(AccountVariables.ActiveDirectoryEndPoint, DefaultAccountEndpoints.OidcAuthContextUri);
}
public AccountType AccountType => AccountType.AzureOidc;
public string GetCredentials => Jwt;
- public string SubscriptionNumber { get; }
+
+ public string SubscriptionNumber { get; }
public string ClientId { get; }
public string TenantId { get; }
public string Jwt { get; }
@@ -55,7 +51,47 @@ public AzureOidcAccount(IVariables variables)
public string ResourceManagementEndpointBaseUri { get; }
public string ActiveDirectoryEndpointBaseUri { get; }
- internal static string GetDefaultScope(string environmentName)
+ public async Task GetAuthorizationToken(ILog log, CancellationToken cancellationToken)
+ {
+ var authClientFactory = new AuthHttpClientFactory();
+
+ var authContext = GetOidcContextUri(string.IsNullOrEmpty(ActiveDirectoryEndpointBaseUri) ? "https://login.microsoftonline.com/" : ActiveDirectoryEndpointBaseUri, TenantId);
+ log.Verbose($"Authentication Context: {authContext}");
+
+ var app = ConfidentialClientApplicationBuilder.Create(ClientId)
+ .WithClientAssertion(GetCredentials)
+ .WithAuthority(authContext)
+ .WithHttpClientFactory(authClientFactory)
+ .Build();
+
+ var result = await app.AcquireTokenForClient(
+ // Default values set on a per cloud basis on AzureOidcAccount, if managementEndPoint is set on the account /.default is required.
+ new[]
+ {
+ ResourceManagementEndpointBaseUri == DefaultAccountEndpoints.ResourceManagementEndpoint || string.IsNullOrEmpty(ResourceManagementEndpointBaseUri)
+ ? AzureOidcAccount.GetDefaultScope(AzureEnvironment)
+ : ResourceManagementEndpointBaseUri.EndsWith(".default")
+ ? ResourceManagementEndpointBaseUri
+ : $"{ResourceManagementEndpointBaseUri}/.default"
+ })
+ .WithTenantId(TenantId)
+ .ExecuteAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ return result.AccessToken;
+ }
+
+ static string GetOidcContextUri(string activeDirectoryEndPoint, string tenantId)
+ {
+ if (!activeDirectoryEndPoint.EndsWith("/"))
+ {
+ return $"{activeDirectoryEndPoint}/{tenantId}/v2.0";
+ }
+
+ return $"{activeDirectoryEndPoint}{tenantId}/v2.0";
+ }
+
+ static string GetDefaultScope(string environmentName)
{
switch (environmentName)
{
@@ -72,28 +108,5 @@ internal static string GetDefaultScope(string environmentName)
return "https://management.azure.com//.default";
}
}
-
- public IAzure CreateAzureClient()
- {
- var environment = string.IsNullOrEmpty(AzureEnvironment) || AzureEnvironment == "AzureCloud"
- ? AzureEnvironmentEnum.AzureGlobalCloud
- : AzureEnvironmentEnum.FromName(AzureEnvironment) ??
- throw new InvalidOperationException($"Unknown environment name {AzureEnvironment}");
-
- var accessToken = this.GetAuthorizationToken(CancellationToken.None).GetAwaiter().GetResult();
- var credentials = new AzureCredentials(
- new TokenCredentials(accessToken),
- new TokenCredentials(accessToken),
- TenantId,
- environment);
-
- // to ensure the Azure API uses the appropriate web proxy
- var client = new HttpClient(new HttpClientHandler {Proxy = NetWebRequest.DefaultWebProxy});
-
- return Microsoft.Azure.Management.Fluent.Azure.Configure()
- .WithHttpClient(client)
- .Authenticate(credentials)
- .WithSubscription(SubscriptionNumber);
- }
}
}
\ No newline at end of file
diff --git a/source/Calamari.CloudAccounts/AzureOidcAccountExtensions.cs b/source/Calamari.CloudAccounts/AzureOidcAccountExtensions.cs
deleted file mode 100644
index ae230f52e..000000000
--- a/source/Calamari.CloudAccounts/AzureOidcAccountExtensions.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Calamari.Common.Plumbing.Logging;
-using Microsoft.Identity.Client;
-using Microsoft.Rest;
-
-namespace Calamari.CloudAccounts
-{
- public static class AzureOidcAccountExtensions
- {
- public static async Task Credentials(this AzureOidcAccount account, CancellationToken cancellationToken)
- {
- return new TokenCredentials(await GetAuthorizationToken(account, cancellationToken));
- }
-
- public static Task GetAuthorizationToken(this AzureOidcAccount account, CancellationToken cancellationToken)
- {
- return GetAuthorizationToken(account.TenantId, account.ClientId, account.GetCredentials,
- account.ResourceManagementEndpointBaseUri, account.ActiveDirectoryEndpointBaseUri, account.AzureEnvironment, cancellationToken);
- }
-
- public static async Task GetAuthorizationToken(string tenantId, string applicationId, string token, string managementEndPoint, string activeDirectoryEndPoint, string aureEnvironment, CancellationToken cancellationToken)
- {
- var authClientFactory = new AuthHttpClientFactory();
-
- var authContext = GetOidcContextUri(string.IsNullOrEmpty(activeDirectoryEndPoint) ? "https://login.microsoftonline.com/" : activeDirectoryEndPoint, tenantId);
- Log.Verbose($"Authentication Context: {authContext}");
-
- var app = ConfidentialClientApplicationBuilder.Create(applicationId)
- .WithClientAssertion(token)
- .WithAuthority(authContext)
- .WithHttpClientFactory(authClientFactory)
- .Build();
-
- var result = await app.AcquireTokenForClient(
- // Default values set on a per cloud basis on AzureOidcAccount, if managementEndPoint is set on the account /.default is required.
- new[] { managementEndPoint == DefaultVariables.ResourceManagementEndpoint || string.IsNullOrEmpty(managementEndPoint) ? AzureOidcAccount.GetDefaultScope(aureEnvironment) : managementEndPoint.EndsWith(".default") ? managementEndPoint : $"{managementEndPoint}/.default" })
- .WithTenantId(tenantId)
- .ExecuteAsync(cancellationToken)
- .ConfigureAwait(false);
- return result.AccessToken;
- }
-
- static string GetOidcContextUri(string activeDirectoryEndPoint, string tenantId)
- {
- if (!activeDirectoryEndPoint.EndsWith("/"))
- {
- return $"{activeDirectoryEndPoint}/{tenantId}/v2.0";
- }
- return $"{activeDirectoryEndPoint}{tenantId}/v2.0";
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.CloudAccounts/AzureServicePrincipalAccount.cs b/source/Calamari.CloudAccounts/AzureServicePrincipalAccount.cs
index 3a77f93ab..34315181c 100644
--- a/source/Calamari.CloudAccounts/AzureServicePrincipalAccount.cs
+++ b/source/Calamari.CloudAccounts/AzureServicePrincipalAccount.cs
@@ -1,11 +1,10 @@
using System;
-using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Calamari.Common.Plumbing.Logging;
using Calamari.Common.Plumbing.Variables;
-using Microsoft.Azure.Management.Fluent;
-using Microsoft.Azure.Management.ResourceManager.Fluent;
+using Microsoft.Identity.Client;
using Newtonsoft.Json;
-using AzureEnvironmentEnum = Microsoft.Azure.Management.ResourceManager.Fluent.AzureEnvironment;
-using NetWebRequest = System.Net.WebRequest;
namespace Calamari.CloudAccounts
{
@@ -37,39 +36,54 @@ public AzureServicePrincipalAccount(IVariables variables)
TenantId = variables.Get(AccountVariables.TenantId);
Password = variables.Get(AccountVariables.Password);
AzureEnvironment = variables.Get(AccountVariables.Environment);
- ResourceManagementEndpointBaseUri = variables.Get(AccountVariables.ResourceManagementEndPoint, DefaultVariables.ResourceManagementEndpoint);
- ActiveDirectoryEndpointBaseUri = variables.Get(AccountVariables.ActiveDirectoryEndPoint, DefaultVariables.ActiveDirectoryEndpoint);
+ ResourceManagementEndpointBaseUri = variables.Get(AccountVariables.ResourceManagementEndPoint, DefaultAccountEndpoints.ResourceManagementEndpoint);
+ ActiveDirectoryEndpointBaseUri = variables.Get(AccountVariables.ActiveDirectoryEndPoint, DefaultAccountEndpoints.ActiveDirectoryEndpoint);
}
public AccountType AccountType => AccountType.AzureServicePrincipal;
public string GetCredentials => Password;
- public string SubscriptionNumber { get; }
+
+ public string SubscriptionNumber { get; }
public string ClientId { get; }
+
public string TenantId { get; }
+
// Public for JsonDeserialization
public string Password { get; }
public string AzureEnvironment { get; }
public string ResourceManagementEndpointBaseUri { get; }
public string ActiveDirectoryEndpointBaseUri { get; }
- public IAzure CreateAzureClient()
+ public async Task GetAuthorizationToken(ILog log, CancellationToken cancellationToken)
{
- var environment = string.IsNullOrEmpty(AzureEnvironment) || AzureEnvironment == "AzureCloud"
- ? AzureEnvironmentEnum.AzureGlobalCloud
- : AzureEnvironmentEnum.FromName(AzureEnvironment) ??
- throw new InvalidOperationException($"Unknown environment name {AzureEnvironment}");
+ var authClientFactory = new AuthHttpClientFactory();
+
+ var authContext = GetContextUri(ActiveDirectoryEndpointBaseUri, TenantId);
+ log.Verbose($"Authentication Context: {authContext}");
- var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(ClientId,
- GetCredentials, TenantId, environment
- );
+ var app = ConfidentialClientApplicationBuilder.Create(ClientId)
+ .WithClientSecret(GetCredentials)
+ .WithAuthority(authContext)
+ .WithHttpClientFactory(authClientFactory)
+ .Build();
- // to ensure the Azure API uses the appropriate web proxy
- var client = new HttpClient(new HttpClientHandler {Proxy = NetWebRequest.DefaultWebProxy});
+ var result = await app.AcquireTokenForClient(
+ new[] { $"{ResourceManagementEndpointBaseUri}/.default" })
+ .WithTenantId(TenantId)
+ .ExecuteAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ return result.AccessToken;
+ }
+
+ static string GetContextUri(string activeDirectoryEndPoint, string tenantId)
+ {
+ if (!activeDirectoryEndPoint.EndsWith("/"))
+ {
+ return $"{activeDirectoryEndPoint}/{tenantId}";
+ }
- return Microsoft.Azure.Management.Fluent.Azure.Configure()
- .WithHttpClient(client)
- .Authenticate(credentials)
- .WithSubscription(SubscriptionNumber);
+ return $"{activeDirectoryEndPoint}{tenantId}";
}
}
-}
+}
\ No newline at end of file
diff --git a/source/Calamari.CloudAccounts/AzureServicePrincipalAccountExtensions.cs b/source/Calamari.CloudAccounts/AzureServicePrincipalAccountExtensions.cs
deleted file mode 100644
index 13344e323..000000000
--- a/source/Calamari.CloudAccounts/AzureServicePrincipalAccountExtensions.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Calamari.Common.Plumbing.Logging;
-using Microsoft.Identity.Client;
-using Microsoft.Rest;
-using AzureEnvironmentEnum = Microsoft.Azure.Management.ResourceManager.Fluent.AzureEnvironment;
-
-namespace Calamari.CloudAccounts
-{
- public static class AzureServicePrincipalAccountExtensions
- {
- public static async Task Credentials(this AzureServicePrincipalAccount account)
- {
- return new TokenCredentials(await GetAuthorizationToken(account));
- }
-
- public static Task GetAuthorizationToken(this AzureServicePrincipalAccount account)
- {
- return GetAuthorizationToken(account.TenantId, account.ClientId, account.GetCredentials,
- account.ResourceManagementEndpointBaseUri, account.ActiveDirectoryEndpointBaseUri);
- }
-
- public static async Task GetAuthorizationToken(string tenantId, string applicationId, string password, string managementEndPoint, string activeDirectoryEndPoint)
- {
- var authClientFactory = new AuthHttpClientFactory();
-
- var authContext = GetContextUri(activeDirectoryEndPoint, tenantId);
- Log.Verbose($"Authentication Context: {authContext}");
-
- var app = ConfidentialClientApplicationBuilder.Create(applicationId)
- .WithClientSecret(password)
- .WithAuthority(authContext)
- .WithHttpClientFactory(authClientFactory)
- .Build();
-
- var result = await app.AcquireTokenForClient(
- new [] { $"{managementEndPoint}/.default" })
- .WithTenantId(tenantId)
- .ExecuteAsync()
- .ConfigureAwait(false);
-
- return result.AccessToken;
- }
-
- static string GetContextUri(string activeDirectoryEndPoint, string tenantId)
- {
- if (!activeDirectoryEndPoint.EndsWith("/"))
- {
- return $"{activeDirectoryEndPoint}/{tenantId}";
- }
- return $"{activeDirectoryEndPoint}{tenantId}";
- }
- }
-}
diff --git a/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj b/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj
index d86a9c838..437288896 100644
--- a/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj
+++ b/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj
@@ -17,10 +17,9 @@
+
-
-
-
+
diff --git a/source/Calamari.CloudAccounts/DefaultVariables.cs b/source/Calamari.CloudAccounts/DefaultVariables.cs
index 70f631d27..8236ca60b 100644
--- a/source/Calamari.CloudAccounts/DefaultVariables.cs
+++ b/source/Calamari.CloudAccounts/DefaultVariables.cs
@@ -2,7 +2,7 @@
namespace Calamari.CloudAccounts
{
- static class DefaultVariables
+ public static class DefaultAccountEndpoints
{
public const string ResourceManagementEndpoint = "https://management.azure.com/";
public const string GraphManagementEndpoint = "https://graph.microsoft.com/";
diff --git a/source/Calamari.CloudAccounts/IAzureAccount.cs b/source/Calamari.CloudAccounts/IAzureAccount.cs
index 66cd204ed..e1b0e926d 100644
--- a/source/Calamari.CloudAccounts/IAzureAccount.cs
+++ b/source/Calamari.CloudAccounts/IAzureAccount.cs
@@ -1,7 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Azure.Management.Fluent;
+using Calamari.Common.Plumbing.Logging;
namespace Calamari.CloudAccounts
{
@@ -16,7 +16,8 @@ public interface IAzureAccount
AccountType AccountType { get; }
string GetCredentials { get; }
- IAzure CreateAzureClient();
+
+ Task GetAuthorizationToken(ILog log, CancellationToken cancellationToken);
}
public enum AccountType
diff --git a/source/Calamari.Testing/RandomAzureRegion.cs b/source/Calamari.Testing/RandomAzureRegion.cs
index ccc064da1..eb2b6fd5e 100644
--- a/source/Calamari.Testing/RandomAzureRegion.cs
+++ b/source/Calamari.Testing/RandomAzureRegion.cs
@@ -5,24 +5,24 @@ namespace Calamari.Testing
{
public static class RandomAzureRegion
{
- static Random random = new Random();
+ static readonly Random Random = new();
- static string[] regions = new[]
- {
+ static readonly string[] Regions = {
"southeastasia",
"centralus",
"eastus",
"eastus2",
"westus",
"westus2",
- "australiaeast"
+ "australiaeast",
+ "australiasoutheast"
};
public static string GetRandomRegionWithExclusions(params string[] excludedRegions)
{
- var possibleRegions = regions.Except(excludedRegions).ToArray();
+ var possibleRegions = Regions.Except(excludedRegions).ToArray();
- return possibleRegions[random.Next(0, possibleRegions.Length)];
+ return possibleRegions[Random.Next(0, possibleRegions.Length)];
}
}
}
\ No newline at end of file