diff --git a/src/Azure.Functions.Cli/Actions/AzureActions/BaseAzureAction.cs b/src/Azure.Functions.Cli/Actions/AzureActions/BaseAzureAction.cs index 00962bba5..02946cfc8 100644 --- a/src/Azure.Functions.Cli/Actions/AzureActions/BaseAzureAction.cs +++ b/src/Azure.Functions.Cli/Actions/AzureActions/BaseAzureAction.cs @@ -1,9 +1,7 @@ using System; -using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; -using Azure.Functions.Cli.Arm; using Azure.Functions.Cli.Common; using static Azure.Functions.Cli.Common.OutputTheme; using Azure.Functions.Cli.Interfaces; @@ -11,8 +9,10 @@ using Fclp; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using static Colors.Net.StringStaticMethods; -using Azure.Functions.Cli.Helpers; +using Azure.Identity; +using Azure.Core; +using System.Threading; +using Azure.ResourceManager; namespace Azure.Functions.Cli.Actions.AzureActions { @@ -21,21 +21,25 @@ abstract class BaseAzureAction : BaseAction, IInitializableAction // Az is the Azure PowerShell module that works in both PowerShell Core and Windows PowerShell private const string _azProfileModuleName = "Az.Accounts"; - // AzureRm is the Azure PowerShell module that only works on Windows PowerShell - private const string _azureRmProfileModuleName = "AzureRM.Profile"; + private const string _defaultManagementURL = Constants.DefaultManagementURL; - // PowerShell Core is version 6.0 and higher that is cross-platform - private const string _powerShellCoreExecutable = "pwsh"; + private TokenCredential _credential; - // Windows PowerShell is PowerShell version 5.1 and lower that only works on Windows - private const string _windowsPowerShellExecutable = "powershell"; + protected TokenCredential Credential => _credential ??= new ChainedTokenCredential( + new AzureCliCredential(new AzureCliCredentialOptions { TenantId = TenantId }), + new AzurePowerShellCredential(new AzurePowerShellCredentialOptions { TenantId = TenantId }) + ); - private const string _defaultManagementURL = Constants.DefaultManagementURL; + private ArmClient _armClient; + + protected ArmClient ArmClient => _armClient ??= new ArmClient(Credential, defaultSubscriptionId: "", + new ArmClientOptions { Environment = new ArmEnvironment(new Uri(ManagementURL), ManagementURL) }); public string AccessToken { get; set; } public bool ReadStdin { get; set; } public string ManagementURL { get; set; } public string Subscription { get; private set; } + public string TenantId { get; private set; } public override ICommandLineParserResult ParseArgs(string[] args) { @@ -55,6 +59,10 @@ public override ICommandLineParserResult ParseArgs(string[] args) .Setup("subscription") .WithDescription("Default subscription to use") .Callback(s => Subscription = s); + Parser + .Setup("tenant-id") + .WithDescription("Azure Tenant ID to use") + .Callback(t => TenantId = t); return base.ParseArgs(args); } @@ -83,14 +91,14 @@ public async Task Initialize() throw new CliException("Stdin unavailable"); } - if (string.IsNullOrEmpty(AccessToken)) + if (string.IsNullOrEmpty(ManagementURL)) { - AccessToken = await GetAccessToken(); + ManagementURL = await GetManagementURL(); } - if (string.IsNullOrEmpty(ManagementURL)) + if (string.IsNullOrEmpty(AccessToken)) { - ManagementURL = await GetManagementURL(); + AccessToken = await GetAccessToken(); } } @@ -126,18 +134,26 @@ private async Task GetManagementURL() private async Task GetAccessToken() { - (bool cliSucceeded, string cliToken) = await TryGetAzCliToken(); - if (cliSucceeded) return cliToken; - - (bool powershellSucceeded, string psToken) = await TryGetAzPowerShellToken(); - if (powershellSucceeded) return psToken; - - if (TryGetTokenFromTestEnvironment(out string envToken)) + try { - return envToken; + var accessToken = await Credential.GetTokenAsync(new TokenRequestContext(new[] { ManagementURL + "/.default" }), CancellationToken.None); + return accessToken.Token; } + catch (Exception ex) + { + if (StaticSettings.IsDebug) + { + ColoredConsole.WriteLine(WarningColor("Unable to fetch access token from CLI")); + ColoredConsole.Error.WriteLine(ErrorColor(ex.ToString())); + } - throw new CliException($"Unable to connect to Azure. Make sure you have the `az` CLI or `{_azProfileModuleName}` PowerShell module installed and logged in and try again"); + if (TryGetTokenFromTestEnvironment(out string envToken)) + { + return envToken; + } + + throw new CliException($"Unable to connect to Azure. Make sure you have the `az` CLI or `{_azProfileModuleName}` PowerShell module installed and logged in and try again"); + } } private bool TryGetTokenFromTestEnvironment(out string token) @@ -146,22 +162,6 @@ private bool TryGetTokenFromTestEnvironment(out string token) return !string.IsNullOrEmpty(token); } - private async Task<(bool succeeded, string token)> TryGetAzCliToken() - { - try - { - return (true, await RunAzCLICommand("account get-access-token --query \"accessToken\" --output json")); - } - catch (Exception) - { - if (StaticSettings.IsDebug) - { - ColoredConsole.WriteLine(WarningColor("Unable to fetch access token from az CLI")); - } - return (false, null); - } - } - private async Task RunAzCLICommand(string param) { if (!CommandChecker.CommandExists("az")) @@ -188,101 +188,5 @@ private async Task RunAzCLICommand(string param) throw new CliException("Error running Az CLI command"); } } - - private async Task<(bool succeeded, string token)> TryGetAzPowerShellToken() - { - // PowerShell Core can only use Az so we can check that it exists and that the Az module exists - if (CommandChecker.CommandExists(_powerShellCoreExecutable) && - await CommandChecker.PowerShellModuleExistsAsync(_powerShellCoreExecutable, _azProfileModuleName)) - { - var az = new Executable(_powerShellCoreExecutable, - $"-NonInteractive -o Text -NoProfile -c {GetPowerShellAccessTokenScript(_azProfileModuleName)}"); - - var stdout = new StringBuilder(); - var stderr = new StringBuilder(); - var exitCode = await az.RunAsync(o => stdout.AppendLine(o), e => stderr.AppendLine(e)); - if (exitCode == 0) - { - return (true, stdout.ToString().Trim(' ', '\n', '\r', '"')); - } - else - { - if (StaticSettings.IsDebug) - { - ColoredConsole.WriteLine(VerboseColor($"Unable to fetch access token from Az.Profile in PowerShell Core. Error: {stderr.ToString().Trim(' ', '\n', '\r')}")); - } - } - } - - // Windows PowerShell can use Az or AzureRM so first we check if powershell.exe is available - if (CommandChecker.CommandExists(_windowsPowerShellExecutable)) - { - string moduleToUse; - - // depending on if Az.Profile or AzureRM.Profile is available, we need to change the prefix - if (await CommandChecker.PowerShellModuleExistsAsync(_windowsPowerShellExecutable, _azProfileModuleName)) - { - moduleToUse = _azProfileModuleName; - } - else if (await CommandChecker.PowerShellModuleExistsAsync(_windowsPowerShellExecutable, _azureRmProfileModuleName)) - { - moduleToUse = _azureRmProfileModuleName; - } - else - { - // User doesn't have either Az.Profile or AzureRM.Profile - if (StaticSettings.IsDebug) - { - ColoredConsole.WriteLine(VerboseColor("Unable to find Az.Profile or AzureRM.Profile.")); - } - return (false, null); - } - - var az = new Executable("powershell", $"-NonInteractive -o Text -NoProfile -c {GetPowerShellAccessTokenScript(moduleToUse)}"); - - var stdout = new StringBuilder(); - var stderr = new StringBuilder(); - var exitCode = await az.RunAsync(o => stdout.AppendLine(o), e => stderr.AppendLine(e)); - if (exitCode == 0) - { - return (true, stdout.ToString().Trim(' ', '\n', '\r', '"')); - } - else - { - if (StaticSettings.IsDebug) - { - ColoredConsole.WriteLine(VerboseColor($"Unable to fetch access token from '{moduleToUse}'. Error: {stderr.ToString().Trim(' ', '\n', '\r')}")); - } - } - } - return (false, null); - } - - // Sets the prefix of the script in case they have Az.Profile or AzureRM.Profile - private static string GetPowerShellAccessTokenScript(string module) - { - string prefix; - if (module == _azProfileModuleName) - { - prefix = "Az"; - } - else if (module == _azureRmProfileModuleName) - { - prefix = "AzureRM"; - } - else - { - throw new ArgumentException($"Expected module to be '{_azProfileModuleName}' or '{_azureRmProfileModuleName}'"); - } - - // This PowerShell script first grabs the Azure context, fetches the profile client and requests an accesstoken. - // This entirely done using the Az.Profile module or AzureRM.Profile - return $@" -$currentAzureContext = Get-{prefix}Context; -$azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile; -$profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient $azureRmProfile; -$profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId).AccessToken; -"; - } } } diff --git a/src/Azure.Functions.Cli/Actions/AzureActions/PublishFunctionAppAction.cs b/src/Azure.Functions.Cli/Actions/AzureActions/PublishFunctionAppAction.cs index 4a7d60cf8..a8bf5a780 100644 --- a/src/Azure.Functions.Cli/Actions/AzureActions/PublishFunctionAppAction.cs +++ b/src/Azure.Functions.Cli/Actions/AzureActions/PublishFunctionAppAction.cs @@ -9,18 +9,19 @@ using System.Threading; using System.Threading.Tasks; using Azure.Functions.Cli.Actions.LocalActions; +using Azure.Functions.Cli.Arm; using Azure.Functions.Cli.Arm.Models; using Azure.Functions.Cli.Common; -using Azure.Functions.Cli.Diagnostics; using Azure.Functions.Cli.Extensions; using Azure.Functions.Cli.Helpers; using Azure.Functions.Cli.Interfaces; +using Azure.ResourceManager; +using Azure.Storage.Blobs; +using Azure.Storage.Blobs.Models; +using Azure.Storage.Sas; using Colors.Net; using Fclp; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Blob; using Newtonsoft.Json; -using NuGet.Common; using static Azure.Functions.Cli.Common.OutputTheme; namespace Azure.Functions.Cli.Actions.AzureActions @@ -290,7 +291,10 @@ private async Task> ValidateFunctionAppPublish(Site } } - if (!functionApp.AzureAppSettings.ContainsKey("AzureWebJobsStorage") && functionApp.IsDynamic && functionApp.IsLinux) + if (!functionApp.AzureAppSettings.ContainsKey(Constants.AzureWebJobsStorage) && + !functionApp.AzureAppSettings.ContainsKey(Constants.AzureWebJobsStorageAccountName) && + functionApp.IsDynamic && + functionApp.IsLinux) { throw new CliException($"Azure Functions Core Tools does not support this deployment path. Please configure the app to deploy from a remote package using the steps here: https://aka.ms/deployfromurl"); } @@ -475,16 +479,11 @@ private async Task PublishFunctionApp(Site functionApp, GitIgnoreParser ignorePa // Flex shouldSyncTriggers = await HandleFlexConsumptionPublish(functionApp, zipStreamFactory); } - else if (functionApp.IsLinux && functionApp.IsElasticPremium) + else if (isFunctionAppDedicatedLinux || (functionApp.IsLinux && functionApp.IsElasticPremium)) { - // Elastic Premium Linux - shouldSyncTriggers = await HandleElasticPremiumLinuxPublish(functionApp, zipStreamFactory); - } - else if (isFunctionAppDedicatedLinux) - { - // Dedicated Linux - shouldSyncTriggers = false; - await HandleLinuxDedicatedPublish(functionApp, zipStreamFactory); + // Elastic Premium / Dedicated Linux + await HandleLinuxElasticOrDedicatedPublish(functionApp, zipStreamFactory); + shouldSyncTriggers = !isFunctionAppDedicatedLinux && PublishBuildOption != BuildOption.Remote; } else if (!functionApp.IsLinux && PublishBuildOption == BuildOption.Remote) { @@ -596,23 +595,7 @@ await WaitForAppSettingUpdateSCM(functionApp, shouldHaveSettings: functionApp.Az } } - private async Task HandleElasticPremiumLinuxPublish(Site functionApp, Func> zipStreamFactory) - { - // Local build - if (PublishBuildOption != BuildOption.Remote) - { - string fileName = string.Format("{0}-{1}", DateTimeOffset.UtcNow.ToString("yyyyMMddHHmmss"), Guid.NewGuid()); - await EnsureNoKuduLiteBuildSettings(functionApp); - await PublishRunFromPackage(functionApp, await zipStreamFactory(), fileName); - return true; - } - - // Remote build - await PerformAppServiceRemoteBuild(zipStreamFactory, functionApp); - return false; - } - - private async Task HandleLinuxDedicatedPublish(Site functionApp, Func> zipStreamFactory) + private async Task HandleLinuxElasticOrDedicatedPublish(Site functionApp, Func> zipStreamFactory) { // Local build if (PublishBuildOption != BuildOption.Remote) @@ -779,11 +762,11 @@ private async Task PublishRunFromPackage(Site functionApp, Stream packageStream, { // Upload zip to blob storage ColoredConsole.WriteLine("Uploading package..."); - var sas = await UploadPackageToStorage(packageStream, fileName, functionApp.AzureAppSettings); + var uri = await UploadPackageToStorage(packageStream, fileName, functionApp.AzureAppSettings); ColoredConsole.WriteLine("Upload completed successfully."); // Set app setting - await SetRunFromPackageAppSetting(functionApp, sas); + await SetRunFromPackageAppSetting(functionApp, uri); ColoredConsole.WriteLine("Deployment completed successfully."); } @@ -1040,15 +1023,25 @@ private async Task UploadPackageToStorage(Stream package, string blobNam const string containerName = "function-releases"; - CloudBlobContainer blobContainer = null; - + BlobContainerClient blobContainerClient = null; try { - var storageConnection = appSettings["AzureWebJobsStorage"]; - var storageAccount = CloudStorageAccount.Parse(storageConnection); - var blobClient = storageAccount.CreateCloudBlobClient(); - blobContainer = blobClient.GetContainerReference(containerName); - await blobContainer.CreateIfNotExistsAsync(); + if (appSettings.TryGetValue(Constants.AzureWebJobsStorageAccountName, out var accountName)) + { + var storageAccount = await ArmClient.FindStorageAccount(accountName); + blobContainerClient = new BlobContainerClient(new UriBuilder(storageAccount.Properties.PrimaryEndpoints.Blob) { Path = containerName }.Uri, + new Storage.StorageSharedKeyCredential(accountName, storageAccount.Key.Value)); + } + else if (appSettings.TryGetValue(Constants.AzureWebJobsStorage, out var connectionString)) + { + blobContainerClient = new BlobContainerClient(connectionString, containerName); + } + else + { + throw new CliException($"Missing {Constants.AzureWebJobsStorageAccountName} / {Constants.AzureWebJobsStorage} configuration values"); + } + + await blobContainerClient.CreateIfNotExistsAsync(); } catch (Exception ex) { @@ -1056,35 +1049,29 @@ private async Task UploadPackageToStorage(Stream package, string blobNam { ColoredConsole.Error.WriteLine(ErrorColor(ex.ToString())); } - throw new CliException($"Error creating a Blob container reference. Please make sure your connection string in \"AzureWebJobsStorage\" is valid"); - } - var blob = blobContainer.GetBlockBlobReference(blobName); - using (var progress = new StorageProgressBar($"Uploading {Utilities.BytesToHumanReadable(package.Length)}", package.Length)) - { - await blob.UploadFromStreamAsync(package, - AccessCondition.GenerateEmptyCondition(), - new BlobRequestOptions(), - new OperationContext(), - progress, - new CancellationToken()); + throw new CliException($"Error creating a blob container client. Please make sure the account name in \"{Constants.AzureWebJobsStorageAccountName}\" or the connection string in \"{Constants.AzureWebJobsStorage}\" is valid."); } - var cloudMd5 = blob.Properties.ContentMD5; + var blob = blobContainerClient.GetBlobClient(blobName); + using var progress = new StorageProgressBar($"Uploading {Utilities.BytesToHumanReadable(package.Length)}", package.Length); + var blobInfo = await blob.UploadAsync(package, new BlobUploadOptions { ProgressHandler = progress }); + + var cloudMd5 = Convert.ToBase64String(blobInfo.Value.ContentHash); if (!cloudMd5.Equals(packageMD5)) { throw new CliException("Upload failed: Integrity error: MD5 hash mismatch between the local copy and the uploaded copy."); } - var sasConstraints = new SharedAccessBlobPolicy(); - sasConstraints.SharedAccessStartTime = DateTimeOffset.UtcNow.AddMinutes(-5); - sasConstraints.SharedAccessExpiryTime = DateTimeOffset.UtcNow.AddYears(10); - sasConstraints.Permissions = SharedAccessBlobPermissions.Read; - - var blobToken = blob.GetSharedAccessSignature(sasConstraints); + var sasConstraints = new BlobSasBuilder + { + StartsOn = DateTimeOffset.UtcNow.AddMinutes(-5), + ExpiresOn = DateTimeOffset.UtcNow.AddYears(10), + }; + sasConstraints.SetPermissions(BlobAccountSasPermissions.Read); - return blob.Uri + blobToken; + return blob.GenerateSasUri(sasConstraints).ToString(); }, 3, TimeSpan.FromSeconds(1), displayError: true); } diff --git a/src/Azure.Functions.Cli/App.config b/src/Azure.Functions.Cli/App.config deleted file mode 100644 index a6435d23a..000000000 --- a/src/Azure.Functions.Cli/App.config +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Azure.Functions.Cli/Arm/ArmClientExtensions.cs b/src/Azure.Functions.Cli/Arm/ArmClientExtensions.cs new file mode 100644 index 000000000..e827dc490 --- /dev/null +++ b/src/Azure.Functions.Cli/Arm/ArmClientExtensions.cs @@ -0,0 +1,42 @@ +using Azure.Functions.Cli.Common; +using Azure.ResourceManager.ResourceGraph; +using Azure.ResourceManager.ResourceGraph.Models; +using Azure.ResourceManager.Storage; +using Azure.ResourceManager.Storage.Models; +using System; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Azure.Functions.Cli.Arm +{ + internal static class ArmClientExtensions + { + private static readonly JsonSerializerOptions s_serializerOptions = new(JsonSerializerDefaults.Web); + + public static async Task FindStorageAccount(this ResourceManager.ArmClient armClient, string accountName) + { + var resources = await armClient.GetTenants().First().GetResourcesAsync( + new ResourceQueryContent( + $"where ['type'] =~ 'microsoft.storage/storageaccounts' and name =~ '{accountName}'")); + + if (resources.Value.Count != 1) + { + throw new CliException($"Unable to locate a storage account named {accountName}"); + } + + var account = resources.Value.Data.ToObjectFromJson(s_serializerOptions).First(); + + await foreach (var key in armClient.GetStorageAccountResource(new(account.Id)).GetKeysAsync()) + { + return account with { Key = key }; + } + + throw new InvalidOperationException("Unreachable"); + } + + public record InternalStorageAccountData(string Id, InternalStorageProperties Properties, StorageAccountKey Key); + public record InternalStorageProperties(InternalStorageAccountEndpoints PrimaryEndpoints); + public record InternalStorageAccountEndpoints(Uri Blob); + } +} diff --git a/src/Azure.Functions.Cli/Arm/ArmClient.cs b/src/Azure.Functions.Cli/Arm/CliArmClient.cs similarity index 98% rename from src/Azure.Functions.Cli/Arm/ArmClient.cs rename to src/Azure.Functions.Cli/Arm/CliArmClient.cs index c393b9bd6..835e7fee8 100644 --- a/src/Azure.Functions.Cli/Arm/ArmClient.cs +++ b/src/Azure.Functions.Cli/Arm/CliArmClient.cs @@ -11,12 +11,12 @@ namespace Azure.Functions.Cli.Arm { - public static class ArmClient + public static class CliArmClient { private static readonly Random _random; private static HttpMessageHandler _testHandler = null; - static ArmClient() + static CliArmClient() { _random = new Random(); } diff --git a/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj b/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj index 594705feb..5fd974fb8 100644 --- a/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj +++ b/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj @@ -276,7 +276,10 @@ - + + + + diff --git a/src/Azure.Functions.Cli/Common/Constants.cs b/src/Azure.Functions.Cli/Common/Constants.cs index c9b7d3790..9e00ed69c 100644 --- a/src/Azure.Functions.Cli/Common/Constants.cs +++ b/src/Azure.Functions.Cli/Common/Constants.cs @@ -32,6 +32,7 @@ internal static class Constants public const string FunctionsExtensionVersion = "FUNCTIONS_EXTENSION_VERSION"; public const string StorageEmulatorConnectionString = "UseDevelopmentStorage=true"; public const string AzureWebJobsStorage = "AzureWebJobsStorage"; + public const string AzureWebJobsStorageAccountName = "AzureWebJobsStorage__accountName"; public const string AzureWebJobsFeatureFlags = "AzureWebJobsFeatureFlags"; public const string PackageReferenceElementName = "PackageReference"; public const string LinuxFxVersion = "linuxFxVersion"; diff --git a/src/Azure.Functions.Cli/Common/SimpleProgressBar.cs b/src/Azure.Functions.Cli/Common/SimpleProgressBar.cs index 7b264289d..3c3484206 100644 --- a/src/Azure.Functions.Cli/Common/SimpleProgressBar.cs +++ b/src/Azure.Functions.Cli/Common/SimpleProgressBar.cs @@ -1,5 +1,4 @@ using Colors.Net; -using Microsoft.WindowsAzure.Storage.Core.Util; using System; namespace Azure.Functions.Cli.Common @@ -71,7 +70,7 @@ public void Dispose() } } - class StorageProgressBar : SimpleProgressBar, IProgress + class StorageProgressBar : SimpleProgressBar, IProgress { private readonly long size; @@ -80,9 +79,9 @@ public StorageProgressBar(string title, long size) : base(title) this.size = size; } - public void Report(StorageProgress value) + void IProgress.Report(long value) { - Report((int)(value.BytesTransferred * 100 / size)); + Report((int)(value * 100 / size)); } } } diff --git a/src/Azure.Functions.Cli/Helpers/AzureHelper.cs b/src/Azure.Functions.Cli/Helpers/AzureHelper.cs index a22f4fe63..d630d6678 100644 --- a/src/Azure.Functions.Cli/Helpers/AzureHelper.cs +++ b/src/Azure.Functions.Cli/Helpers/AzureHelper.cs @@ -112,7 +112,7 @@ internal static async Task GetResourceIDFromArg(IEnumerable subI query }; - var response = await ArmClient.HttpInvoke(HttpMethod.Post, url, accessToken, objectPayload: bodyObject); + var response = await CliArmClient.HttpInvoke(HttpMethod.Post, url, accessToken, objectPayload: bodyObject); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); @@ -128,7 +128,7 @@ internal static async Task IsBasicAuthAllowedForSCM(Site functionApp, stri { var url = new Uri($"{managementURL}{functionApp.SiteId}/basicPublishingCredentialsPolicies/scm?api-version={ArmUriTemplates.BasicAuthCheckApiVersion}"); - var response = await ArmClient.HttpInvoke(HttpMethod.Get, url, accessToken); + var response = await CliArmClient.HttpInvoke(HttpMethod.Get, url, accessToken); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); @@ -361,7 +361,7 @@ await ArmHttpAsync>(HttpMethod.Get, url, accessToke public static Task SyncTriggers(Site functionApp, string accessToken, string managementURL) { var url = new Uri($"{managementURL}{functionApp.SiteId}/host/default/sync?api-version={ArmUriTemplates.WebsitesApiVersion}"); - return ArmClient.HttpInvoke(HttpMethod.Post, url, accessToken); + return CliArmClient.HttpInvoke(HttpMethod.Post, url, accessToken); } internal static async Task CheckFunctionHostStatusForFlex(Site functionApp, string accessToken, string managementURL, @@ -427,7 +427,7 @@ public static async Task LoadSiteObjectAsync(Site site, string accessToken private static async Task ArmHttpAsync(HttpMethod method, Uri uri, string accessToken, object payload = null) { - var response = await ArmClient.HttpInvoke(method, uri, accessToken, payload, retryCount: 3); + var response = await CliArmClient.HttpInvoke(method, uri, accessToken, payload, retryCount: 3); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsAsync(); @@ -435,14 +435,14 @@ private static async Task ArmHttpAsync(HttpMethod method, Uri uri, string private static async Task ArmHttpAsync(HttpMethod method, Uri uri, string accessToken, object payload = null) { - var response = await ArmClient.HttpInvoke(method, uri, accessToken, payload, retryCount: 3); + var response = await CliArmClient.HttpInvoke(method, uri, accessToken, payload, retryCount: 3); response.EnsureSuccessStatusCode(); } public static async Task> UpdateWebSettings(Site site, Dictionary webSettings, string accessToken, string managementURL) { var url = new Uri($"{managementURL}{site.SiteId}/config/web?api-version={ArmUriTemplates.WebsitesApiVersion}"); - var response = await ArmClient.HttpInvoke(HttpMethod.Put, url, accessToken, new { properties = webSettings }); + var response = await CliArmClient.HttpInvoke(HttpMethod.Put, url, accessToken, new { properties = webSettings }); if (response.IsSuccessStatusCode) { // Simply reading it as a string because we do not care about the result content particularly @@ -463,7 +463,7 @@ public static async Task> UpdateWebSettings(Site site public static async Task, string>> UpdateFunctionAppAppSettings(Site site, string accessToken, string managementURL) { var url = new Uri($"{managementURL}{site.SiteId}/config/AppSettings?api-version={ArmUriTemplates.WebsitesApiVersion}"); - var response = await ArmClient.HttpInvoke(HttpMethod.Put, url, accessToken, new { properties = site.AzureAppSettings }); + var response = await CliArmClient.HttpInvoke(HttpMethod.Put, url, accessToken, new { properties = site.AzureAppSettings }); if (response.IsSuccessStatusCode) { var result = await response.Content.ReadAsAsync>>(); @@ -544,7 +544,7 @@ public static async Task CreateFunctionAppOnContainerService(string acce string hostName = string.Empty; var url = new Uri($"{managementURL}/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/sites/{payload.Name}?api-version={ArmUriTemplates.FunctionAppOnContainerAppsApiVersion}"); ColoredConsole.WriteLine(Constants.FunctionAppDeploymentToContainerAppsMessage); - var response = await ArmClient.HttpInvoke(HttpMethod.Put, url, accessToken, payload); + var response = await CliArmClient.HttpInvoke(HttpMethod.Put, url, accessToken, payload); if (!response.IsSuccessStatusCode) { string errorMessage; @@ -573,7 +573,7 @@ public static async Task CreateFunctionAppOnContainerService(string acce int maxRetries = 12; // 12 * 5 seconds = 1 minute for (int retry = 1; retry <= maxRetries; retry++) { - var getResponse = await ArmClient.HttpInvoke(HttpMethod.Get, statusUrl, accessToken, payload); + var getResponse = await CliArmClient.HttpInvoke(HttpMethod.Get, statusUrl, accessToken, payload); if (getResponse.StatusCode != System.Net.HttpStatusCode.Accepted) { getResponse.EnsureSuccessStatusCode(); @@ -662,7 +662,7 @@ public static async Task PrintFunctionsInfo(Site functionApp, string accessToken public static async Task<(string, string)> GetManagedEnvironmentInfo(string accessToken, string managementURL, string subscriptionId, string resourceGroup, string name) { var url = new Uri($"{managementURL}/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.App/managedEnvironments/{name}?api-version={ArmUriTemplates.ManagedEnvironmentApiVersion}"); - var response = await ArmClient.HttpInvoke(HttpMethod.Get, url, accessToken); + var response = await CliArmClient.HttpInvoke(HttpMethod.Get, url, accessToken); var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) diff --git a/test/Azure.Functions.Cli.Tests/AzureHelperTests.cs b/test/Azure.Functions.Cli.Tests/AzureHelperTests.cs index f712965f8..2672bb209 100644 --- a/test/Azure.Functions.Cli.Tests/AzureHelperTests.cs +++ b/test/Azure.Functions.Cli.Tests/AzureHelperTests.cs @@ -27,14 +27,14 @@ public async Task GetFunctionKeyTest(string key, string value) mockHttp.When(HttpMethod.Post, mockUrl) .Respond("application/json", $"{{'{key}': '{value}'}}"); - ArmClient.SetTestHandler(mockHttp); + CliArmClient.SetTestHandler(mockHttp); var result = await AzureHelper.GetFunctionKey(functionName, appId, accessToken, managementUrl); result.Should().Be(value); } finally { - ArmClient.SetTestHandler(null); + CliArmClient.SetTestHandler(null); } } } diff --git a/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/FunctionAppManager.cs b/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/FunctionAppManager.cs index a9e48dc4e..73782d56b 100644 --- a/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/FunctionAppManager.cs +++ b/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/FunctionAppManager.cs @@ -18,7 +18,7 @@ public async Task Get( FunctionAppOs os = GetOsFromResourceLabel(name); string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/sites/{name}?api-version=2018-11-01"); - return await ArmClient.HttpInvoke("GET", uri, AccessToken); + return await CliArmClient.HttpInvoke("GET", uri, AccessToken); } public async Task Create( @@ -57,7 +57,7 @@ public async Task Create( } }; - var response = await ArmClient.HttpInvoke("PUT", uri, AccessToken, payload); + var response = await CliArmClient.HttpInvoke("PUT", uri, AccessToken, payload); if (response.IsSuccessStatusCode) { AddToResources(name, os); @@ -77,7 +77,7 @@ public async Task Delete( string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}/subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/sites/{name}?api-version=2018-11-01"); - var response = await ArmClient.HttpInvoke("DELETE", uri, AccessToken); + var response = await CliArmClient.HttpInvoke("DELETE", uri, AccessToken); if (response.IsSuccessStatusCode) { RemoveFromResources(name, os); @@ -100,7 +100,7 @@ public async Task CheckIfSiteExistsFromArg( query = $"where type =~ \'Microsoft.Web/sites\' and name =~ \'{name}\'" }; - var response = await ArmClient.HttpInvoke(HttpMethod.Post, url, AccessToken, payload); + var response = await CliArmClient.HttpInvoke(HttpMethod.Post, url, AccessToken, payload); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); @@ -149,7 +149,7 @@ public async Task WaitUntilScmSiteAvailable( Uri uri = new Uri($"https://{name}.scm.azurewebsites.net"); await RetryHelper.Retry(async () => { - HttpResponseMessage response = await ArmClient.HttpInvoke("GET", uri, AccessToken); + HttpResponseMessage response = await CliArmClient.HttpInvoke("GET", uri, AccessToken); response.EnsureSuccessStatusCode(); }, retryCount: numOfRetries, retryDelay: TimeSpan.FromSeconds(retryIntervalSec)); } diff --git a/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/ServerFarmManager.cs b/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/ServerFarmManager.cs index 1262223ad..7482b0bb6 100644 --- a/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/ServerFarmManager.cs +++ b/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/ServerFarmManager.cs @@ -17,7 +17,7 @@ public async Task Get( FunctionAppOs os = GetOsFromResourceLabel(name); string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/serverfarms/{name}?api-version=2019-08-01"); - return await ArmClient.HttpInvoke("GET", uri, AccessToken); + return await CliArmClient.HttpInvoke("GET", uri, AccessToken); } public async Task Create( @@ -35,7 +35,7 @@ public async Task Create( properties = sku.GetServerFarmProperties(os), sku = sku.GetServerFarmSku(os) }; - var response = await ArmClient.HttpInvoke("PUT", uri, AccessToken, payload); + var response = await CliArmClient.HttpInvoke("PUT", uri, AccessToken, payload); if (response.IsSuccessStatusCode) { AddToResources(name, os); @@ -55,7 +55,7 @@ public async Task Delete( string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/serverfarms/{name}?api-version=2019-08-01"); - var response = await ArmClient.HttpInvoke("DELETE", uri, AccessToken); + var response = await CliArmClient.HttpInvoke("DELETE", uri, AccessToken); if (response.IsSuccessStatusCode) { RemoveFromResources(name, os); diff --git a/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/StorageAccountManager.cs b/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/StorageAccountManager.cs index eec0ca8d7..86e89b0b4 100644 --- a/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/StorageAccountManager.cs +++ b/test/Azure.Functions.Cli.Tests/E2E/AzureResourceManagers/StorageAccountManager.cs @@ -25,7 +25,7 @@ public async Task Get( FunctionAppOs os = GetOsFromResourceLabel(name); string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Storage/storageAccounts/{name}?api-version=2019-06-01"); - return await ArmClient.HttpInvoke("GET", uri, AccessToken); + return await CliArmClient.HttpInvoke("GET", uri, AccessToken); } public async Task ListKeys( @@ -34,7 +34,7 @@ public async Task ListKeys( FunctionAppOs os = GetOsFromResourceLabel(name); string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Storage/storageAccounts/{name}/listkeys?api-version=2019-06-01"); - var response = await ArmClient.HttpInvoke("POST", uri, AccessToken); + var response = await CliArmClient.HttpInvoke("POST", uri, AccessToken); if (response.IsSuccessStatusCode) { string json = await response.Content.ReadAsStringAsync(); @@ -64,7 +64,7 @@ public async Task Create( name = "Standard_LRS" } }; - var response = await ArmClient.HttpInvoke("PUT", uri, AccessToken, payload); + var response = await CliArmClient.HttpInvoke("PUT", uri, AccessToken, payload); if (response.IsSuccessStatusCode) { AddToResources(name, os); @@ -83,7 +83,7 @@ public async Task Delete( FunctionAppOs os = GetOsFromResourceLabel(name); string resourceGroup = GetResourceGroupName(os); Uri uri = new Uri($"{ManagementURL}subscriptions/{SubscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Storage/storageAccounts/{name}?api-version=2019-06-01"); - var response = await ArmClient.HttpInvoke("DELETE", uri, AccessToken); + var response = await CliArmClient.HttpInvoke("DELETE", uri, AccessToken); if (response.IsSuccessStatusCode) { RemoveFromResources(name, os); diff --git a/test/Azure.Functions.Cli.Tests/app.config b/test/Azure.Functions.Cli.Tests/app.config deleted file mode 100644 index 19852ceb2..000000000 --- a/test/Azure.Functions.Cli.Tests/app.config +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -