update according to comments
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.4" />
|
||||
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
|
||||
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.1" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.6.0" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.9.0-beta.2" />
|
||||
<PackageVersion Include="BlazorInputFile" Version="0.2.0" />
|
||||
<PackageVersion Include="Blazored.LocalStorage" Version="4.3.0" />
|
||||
<PackageVersion Include="BuildBundlerMinifier" Version="3.2.449" PrivateAssets="All" />
|
||||
|
||||
14
README.md
14
README.md
@@ -63,20 +63,28 @@ powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1'
|
||||
curl -fsSL https://aka.ms/install-azd.sh | bash
|
||||
```
|
||||
|
||||
And you can also install with package managers, like winget, choco, and brew. For more detials, you can follow the documentation: https://aka.ms/azure-dev/install.
|
||||
|
||||
After logging in with the following command, you will be able to use the azd cli to quickly provision and deploy the application.
|
||||
|
||||
```
|
||||
azd login
|
||||
```
|
||||
|
||||
Then, just use the `azd up` command to complete all operations of clone, provision and deployment.
|
||||
Then, executes the commands `azd init` to initializes environment.
|
||||
```
|
||||
azd up -t dotnet-architecture/eShopOnWeb
|
||||
azd init -t dotnet-architecture/eShopOnWeb
|
||||
```
|
||||
|
||||
And executes the commands `azd up` to complete all operations of provision and deployment.
|
||||
```
|
||||
azd up
|
||||
```
|
||||
|
||||
According to the prompt, enter an `env name`, and select `subscription` and `location`, these are the necessary parameters when you create resources. Wait a moment for the resource deployment to complete, click the web endpoint and you will see the home page.
|
||||
|
||||
**Notes:**
|
||||
Considering security, we store its related data (id, password) in the key vault when we create the database, and obtain it from the key vault when we use it. This is different from directly deploying applications locally.
|
||||
Considering security, we store its related data (id, password) in the **Azure Key Vault** when we create the database, and obtain it from the Key Vault when we use it. This is different from directly deploying applications locally.
|
||||
|
||||
You can also run the sample directly locally (See below).
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
|
||||
resource firewall 'firewallRules' = {
|
||||
name: 'Azure Services'
|
||||
properties: {
|
||||
// Allow all clients
|
||||
// Allow all clients
|
||||
// Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only".
|
||||
// This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes.
|
||||
startIpAddress: '0.0.0.1'
|
||||
@@ -127,4 +127,3 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
|
||||
var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}'
|
||||
output connectionStringKey string = connectionStringKey
|
||||
output databaseName string = sqlServer::database.name
|
||||
output connectionString string = connectionString
|
||||
|
||||
@@ -32,6 +32,8 @@ param minimumElasticInstanceCount int = -1
|
||||
param numberOfWorkers int = -1
|
||||
param scmDoBuildDuringDeployment bool = false
|
||||
param use32BitWorkerProcess bool = false
|
||||
param ftpsState string = 'FtpsOnly'
|
||||
param healthCheckPath string = ''
|
||||
|
||||
resource appService 'Microsoft.Web/sites@2022-03-01' = {
|
||||
name: name
|
||||
@@ -43,12 +45,14 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = {
|
||||
siteConfig: {
|
||||
linuxFxVersion: linuxFxVersion
|
||||
alwaysOn: alwaysOn
|
||||
ftpsState: 'FtpsOnly'
|
||||
ftpsState: ftpsState
|
||||
minTlsVersion: '1.2'
|
||||
appCommandLine: appCommandLine
|
||||
numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null
|
||||
minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null
|
||||
use32BitWorkerProcess: use32BitWorkerProcess
|
||||
functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null
|
||||
healthCheckPath: healthCheckPath
|
||||
cors: {
|
||||
allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
param name string = 'add'
|
||||
|
||||
param keyVaultName string = ''
|
||||
param keyVaultName string
|
||||
param permissions object = { secrets: [ 'get', 'list' ] }
|
||||
param principalId string
|
||||
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
using Azure.Core;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Azure.Identity
|
||||
{
|
||||
public class AzureDeveloperCliCredential : TokenCredential
|
||||
{
|
||||
internal const string AzdCliNotInstalled = $"Azure Developer CLI could not be found. {Troubleshoot}";
|
||||
internal const string AzdNotLogIn = "Please run 'azd login' from a command prompt to authenticate before using this credential.";
|
||||
internal const string WinAzdCliError = "'azd is not recognized";
|
||||
internal const string AzdCliTimeoutError = "Azure Developer CLI authentication timed out.";
|
||||
internal const string AzdCliFailedError = "Azure Developer CLI authentication failed due to an unknown error.";
|
||||
internal const string Troubleshoot = "Please visit https://aka.ms/azure-dev for installation instructions and then, once installed, authenticate to your Azure account using 'azd login'.";
|
||||
internal const string InteractiveLoginRequired = "Azure Developer CLI could not login. Interactive login is required.";
|
||||
private const string RefreshTokeExpired = "The provided authorization code or refresh token has expired due to inactivity. Send a new interactive authorization request for this user and resource.";
|
||||
|
||||
private static readonly string DefaultWorkingDirWindows = Environment.GetFolderPath(Environment.SpecialFolder.System);
|
||||
private const string DefaultWorkingDirNonWindows = "/bin/";
|
||||
private static readonly string DefaultWorkingDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? DefaultWorkingDirWindows : DefaultWorkingDirNonWindows;
|
||||
|
||||
private static readonly Regex AzdNotFoundPattern = new Regex("azd:(.*)not found");
|
||||
|
||||
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RequestCliAccessTokenAsync(requestContext, cancellationToken)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await RequestCliAccessTokenAsync(requestContext, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async ValueTask<AccessToken> RequestCliAccessTokenAsync(TokenRequestContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo processStartInfo = GetAzdCliProcessStartInfo(context.Scopes);
|
||||
string output = await RunProcessAsync(processStartInfo);
|
||||
|
||||
return DeserializeOutput(output);
|
||||
}
|
||||
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw new AuthenticationFailedException(AzdCliTimeoutError);
|
||||
}
|
||||
catch (InvalidOperationException exception)
|
||||
{
|
||||
bool isWinError = exception.Message.StartsWith(WinAzdCliError, StringComparison.CurrentCultureIgnoreCase);
|
||||
bool isOtherOsError = AzdNotFoundPattern.IsMatch(exception.Message);
|
||||
|
||||
if (isWinError || isOtherOsError)
|
||||
{
|
||||
throw new CredentialUnavailableException(AzdCliNotInstalled);
|
||||
}
|
||||
|
||||
bool isAADSTSError = exception.Message.Contains("AADSTS");
|
||||
bool isLoginError = exception.Message.IndexOf("azd login", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
|
||||
if (isLoginError && !isAADSTSError)
|
||||
{
|
||||
throw new CredentialUnavailableException(AzdNotLogIn);
|
||||
}
|
||||
|
||||
bool isRefreshTokenFailedError = exception.Message.IndexOf(AzdCliFailedError, StringComparison.OrdinalIgnoreCase) != -1 &&
|
||||
exception.Message.IndexOf(RefreshTokeExpired, StringComparison.OrdinalIgnoreCase) != -1 ||
|
||||
exception.Message.IndexOf("CLIInternalError", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
|
||||
if (isRefreshTokenFailedError)
|
||||
{
|
||||
throw new CredentialUnavailableException(InteractiveLoginRequired);
|
||||
}
|
||||
|
||||
throw new AuthenticationFailedException($"{AzdCliFailedError} {Troubleshoot} {exception.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new CredentialUnavailableException($"{AzdCliFailedError} {Troubleshoot} {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<string> RunProcessAsync(ProcessStartInfo processStartInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var process = Process.Start(processStartInfo);
|
||||
if (process == null)
|
||||
{
|
||||
throw new CredentialUnavailableException(AzdCliFailedError);
|
||||
}
|
||||
|
||||
await process.WaitForExitAsync(cancellationToken);
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
var errorMessage = process.StandardError.ReadToEnd();
|
||||
throw new InvalidOperationException(errorMessage);
|
||||
}
|
||||
|
||||
return process.StandardOutput.ReadToEnd();
|
||||
}
|
||||
|
||||
private ProcessStartInfo GetAzdCliProcessStartInfo(string[] scopes)
|
||||
{
|
||||
string scopeArgs = string.Join(" ", scopes.Select(scope => string.Format($"--scope {scope}")));
|
||||
string command = $"azd auth token --output json {scopeArgs}";
|
||||
|
||||
string fileName;
|
||||
string argument;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
|
||||
argument = $"/c \"{command}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName = "/bin/sh";
|
||||
argument = $"-c \"{command}\"";
|
||||
}
|
||||
|
||||
return new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = argument,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
ErrorDialog = false,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = DefaultWorkingDir,
|
||||
};
|
||||
}
|
||||
|
||||
private static AccessToken DeserializeOutput(string output)
|
||||
{
|
||||
using JsonDocument document = JsonDocument.Parse(output);
|
||||
|
||||
JsonElement root = document.RootElement;
|
||||
string accessToken = root.GetProperty("token").GetString();
|
||||
DateTimeOffset expiresOn = root.GetProperty("expiresOn").GetDateTimeOffset();
|
||||
|
||||
return new AccessToken(accessToken, expiresOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user