diff --git a/src/Accounts/Accounts/Accounts.format.ps1xml b/src/Accounts/Accounts/Accounts.format.ps1xml index 60a05a222f74..ec0d3787fa7d 100644 --- a/src/Accounts/Accounts/Accounts.format.ps1xml +++ b/src/Accounts/Accounts/Accounts.format.ps1xml @@ -171,6 +171,35 @@ + + Microsoft.Azure.Commands.Profile.Models.PSAccessToken + + Microsoft.Azure.Commands.Profile.Models.PSAccessToken + + + + + + + Token + + + ExpiresOn + + + Type + + + TenantId + + + UserId + + + + + + Microsoft.Azure.Commands.Profile.Models.PSAzureSubscriptionPolicy diff --git a/src/Accounts/Accounts/Az.Accounts.psd1 b/src/Accounts/Accounts/Az.Accounts.psd1 index 7d9b3f57f530..ce7b5ca2171b 100644 --- a/src/Accounts/Accounts/Az.Accounts.psd1 +++ b/src/Accounts/Accounts/Az.Accounts.psd1 @@ -106,7 +106,7 @@ CmdletsToExport = 'Disable-AzDataCollection', 'Disable-AzContextAutosave', 'Disconnect-AzAccount', 'Get-AzContextAutosaveSetting', 'Set-AzDefault', 'Get-AzDefault', 'Clear-AzDefault', 'Register-AzModule', 'Enable-AzureRmAlias', 'Disable-AzureRmAlias', - 'Uninstall-AzureRm', 'Invoke-AzRestMethod' + 'Uninstall-AzureRm', 'Invoke-AzRestMethod', 'Get-AzAccessToken' # Variables to export from this module # VariablesToExport = @() diff --git a/src/Accounts/Accounts/ChangeLog.md b/src/Accounts/Accounts/ChangeLog.md index 57d4388cdded..9389507f3feb 100644 --- a/src/Accounts/Accounts/ChangeLog.md +++ b/src/Accounts/Accounts/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Upcoming Release +* Added new cmdlet `Get-AzAccessToken` * Fixed an issue that error happens if user profile path is inaccessible * Fixed an issue causing Write-Object error during Connect-AzAccount [#13419] * Added parameter "ContainerRegistryEndpointSuffix" to: `Add-AzEnvironment`, `Set-AzEnvironment` diff --git a/src/Accounts/Accounts/Models/PSAccessToken.cs b/src/Accounts/Accounts/Models/PSAccessToken.cs new file mode 100644 index 000000000000..959de7f4255d --- /dev/null +++ b/src/Accounts/Accounts/Models/PSAccessToken.cs @@ -0,0 +1,30 @@ +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.Azure.Commands.Profile.Models +{ + public class PSAccessToken + { + public string Token { get; set; } + + public DateTimeOffset ExpiresOn { get; set; } + + public string TenantId { get; set; } + + public string UserId { get; set; } + + public string Type { get; } = "Bearer"; + } +} diff --git a/src/Accounts/Accounts/Properties/Resources.Designer.cs b/src/Accounts/Accounts/Properties/Resources.Designer.cs index 998663f21f28..7e639f21e1cc 100644 --- a/src/Accounts/Accounts/Properties/Resources.Designer.cs +++ b/src/Accounts/Accounts/Properties/Resources.Designer.cs @@ -600,6 +600,15 @@ internal static string InvalidEndpointProvided { } } + /// + /// Looks up a localized string similar to The specified ResourceTypeName "{0}" is not supported, please provide a valid value. e.g. Arm, AadGraph, etc.. + /// + internal static string InvalidResourceTypeName { + get { + return ResourceManager.GetString("InvalidResourceTypeName", resourceCulture); + } + } + /// /// Looks up a localized string similar to The provided subscription ID "{0}" is not a valid Guid.. /// diff --git a/src/Accounts/Accounts/Properties/Resources.resx b/src/Accounts/Accounts/Properties/Resources.resx index b18d22752931..48a66d7b008a 100644 --- a/src/Accounts/Accounts/Properties/Resources.resx +++ b/src/Accounts/Accounts/Properties/Resources.resx @@ -519,6 +519,9 @@ Please run 'Connect-AzAccount -DeviceCode' if browser is not supported in this session. + + The specified ResourceTypeName "{0}" is not supported, please provide a valid value. e.g. Arm, AadGraph, etc. + INITIALIZATION: Fallback context save mode to process because of error during checking token cache persistence: {0}. diff --git a/src/Accounts/Accounts/Token/GetAzureRmAccessToken.cs b/src/Accounts/Accounts/Token/GetAzureRmAccessToken.cs new file mode 100644 index 000000000000..d493f27787a4 --- /dev/null +++ b/src/Accounts/Accounts/Token/GetAzureRmAccessToken.cs @@ -0,0 +1,147 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Management.Automation; + +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Commands.Profile.Models; +using Microsoft.Azure.Commands.ResourceManager.Common; +using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters; +using Microsoft.Azure.PowerShell.Authenticators; + +namespace Microsoft.Azure.Commands.Profile +{ + [Cmdlet(VerbsCommon.Get, AzureRMConstants.AzureRMPrefix + "AccessToken", DefaultParameterSetName = KnownResourceNameParameterSet)] + [OutputType(typeof(PSAccessToken))] + public class GetAzureRmAccessTokenCommand : AzureRMCmdlet + { + private const string ResourceUrlParameterSet = "ResourceUrl"; + private const string KnownResourceNameParameterSet = "KnownResourceTypeName"; + + [Parameter(ParameterSetName = ResourceUrlParameterSet, + Mandatory = true, + HelpMessage = "Resource url for that you're requesting token, e.g. 'http://graph.windows.net/'.")] + [ValidateNotNullOrEmpty] + [Alias("Resource", "ResourceUri")] + public string ResourceUrl { get; set; } + + [Parameter(ParameterSetName = KnownResourceNameParameterSet, + Mandatory = false, + HelpMessage = "Optional resouce type name, supported values: AadGraph, AnalysisServices, Arm, Attestation, Batch, DataLake, KeyVault, OperationalInsights, ResourceManager, Synapse. Default value is Arm if not specified.")] + [PSArgumentCompleter( + SupportedResourceNames.AadGraph, + SupportedResourceNames.AnalysisServices, + SupportedResourceNames.Arm, + SupportedResourceNames.Attestation, + SupportedResourceNames.Batch, + SupportedResourceNames.DataLake, + SupportedResourceNames.KeyVault, + SupportedResourceNames.ManagedHsm, + SupportedResourceNames.OperationalInsights, + SupportedResourceNames.ResourceManager, + SupportedResourceNames.Synapse + )] + public string ResourceTypeName { get; set; } + + //Use tenant in default context if not specified + //TODO: Should not specify TenantId for MSI, CloudShell(?) + [Parameter(Mandatory = false, HelpMessage = "Optional Tenant Id. Use tenant id of default context if not specified.")] + public string TenantId { get; set; } + + public override void ExecuteCmdlet() + { + base.ExecuteCmdlet(); + + string resourceUrlOrId; + + if (ParameterSetName == KnownResourceNameParameterSet) + { + if (ResourceTypeName == null) + { + ResourceTypeName = SupportedResourceNames.Arm; + } + if (!SupportedResourceNames.ResourceNameMap.ContainsKey(ResourceTypeName)) + { + throw new ArgumentException(Properties.Resources.InvalidResourceTypeName.FormatInvariant(ResourceTypeName), nameof(ResourceTypeName)); + } + resourceUrlOrId = SupportedResourceNames.ResourceNameMap[ResourceTypeName]; + } + else + { + resourceUrlOrId = ResourceUrl; + } + + IAzureContext context = DefaultContext; + if(TenantId == null) + { + TenantId = context.Tenant?.Id; + } + + IAccessToken accessToken = AzureSession.Instance.AuthenticationFactory.Authenticate( + context.Account, + context.Environment, + TenantId, + null, + ShowDialog.Never, + null, + null, + resourceUrlOrId); + + var result = new PSAccessToken() + { + Token = accessToken.AccessToken, + TenantId = TenantId, + UserId = accessToken.UserId, + }; + result.ExpiresOn = (accessToken as MsalAccessToken)?.ExpiresOn ?? result.ExpiresOn; + + WriteObject(result); + } + + internal class SupportedResourceNames + { + public const string Arm = "Arm"; + public const string AadGraph = "AadGraph"; + public const string Batch = "Batch"; + public const string DataLake = "DataLake"; + public const string KeyVault = "KeyVault"; + public const string ResourceManager = "ResourceManager"; //endpoint is same as Arm + + public const string AnalysisServices = "AnalysisServices"; + public const string Attestation = "Attestation"; + public const string OperationalInsights = "OperationalInsights"; + public const string Synapse = "Synapse"; + public const string ManagedHsm = "ManagedHsm"; + + internal static Dictionary ResourceNameMap = new Dictionary() + { + { Arm, AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId }, + { AadGraph, AzureEnvironment.Endpoint.GraphEndpointResourceId }, + { Batch, AzureEnvironment.Endpoint.BatchEndpointResourceId }, + { DataLake, AzureEnvironment.Endpoint.DataLakeEndpointResourceId }, + { KeyVault, AzureEnvironment.Endpoint.AzureKeyVaultServiceEndpointResourceId }, + { ResourceManager, AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId }, + + { AnalysisServices, AzureEnvironment.ExtendedEndpoint.AnalysisServicesEndpointResourceId }, + { Attestation, AzureEnvironment.ExtendedEndpoint.AzureAttestationServiceEndpointResourceId }, + { OperationalInsights, AzureEnvironment.ExtendedEndpoint.OperationalInsightsEndpointResourceId }, + { Synapse, AzureEnvironment.ExtendedEndpoint.AzureSynapseAnalyticsEndpointResourceId }, + { ManagedHsm, AzureEnvironment.ExtendedEndpoint.ManagedHsmServiceEndpointResourceId } + }; + } + } +} diff --git a/src/Accounts/Accounts/help/Az.Accounts.md b/src/Accounts/Accounts/help/Az.Accounts.md index 40662c53cdef..b18d027df9a4 100644 --- a/src/Accounts/Accounts/help/Az.Accounts.md +++ b/src/Accounts/Accounts/help/Az.Accounts.md @@ -47,6 +47,9 @@ machine. Data is collected by default unless you explicitly opt out. ### [Enable-AzureRmAlias](Enable-AzureRmAlias.md) Enables AzureRm prefix aliases for Az modules. +### [Get-AzAccessToken](Get-AzAccessToken.md) +Get raw access token. + ### [Get-AzContext](Get-AzContext.md) Gets the metadata used to authenticate Azure Resource Manager requests. @@ -60,9 +63,6 @@ Get the defaults set by the user in the current context. ### [Get-AzEnvironment](Get-AzEnvironment.md) Get endpoints and metadata for an instance of Azure services. -### [Get-AzProfile](Get-AzProfile.md) -Get the service profiles supported by installed modules. - ### [Get-AzSubscription](Get-AzSubscription.md) Get subscriptions that the current account can access. @@ -96,9 +96,6 @@ Saves the current authentication information for use in other PowerShell session ### [Select-AzContext](Select-AzContext.md) Select a subscription and account to target in Azure PowerShell cmdlets -### [Select-AzProfile](Select-AzProfile.md) -For modules that support multiple service profiles - load the cmdlets corresponding with the given service profile. - ### [Send-Feedback](Send-Feedback.md) Sends feedback to the Azure PowerShell team via a set of guided prompts. diff --git a/src/Accounts/Accounts/help/Get-AzAccessToken.md b/src/Accounts/Accounts/help/Get-AzAccessToken.md new file mode 100644 index 000000000000..5c09aa4ccdec --- /dev/null +++ b/src/Accounts/Accounts/help/Get-AzAccessToken.md @@ -0,0 +1,128 @@ +--- +external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml +Module Name: Az.Accounts +online version: https://docs.microsoft.com/en-us/powershell/module/az.accounts/get-azaccesstoken +schema: 2.0.0 +--- + +# Get-AzAccessToken + +## SYNOPSIS +Get raw access token. When using -ResourceUrl, please make sure the value does match current Azure environment. You may refer to the value of `(Get-AzContext).Environment`. + +## SYNTAX + +### KnownResourceTypeName (Default) +``` +Get-AzAccessToken [-ResourceTypeName ] [-TenantId ] [-DefaultProfile ] + [] +``` + +### ResourceUrl +``` +Get-AzAccessToken -ResourceUrl [-TenantId ] [-DefaultProfile ] + [] +``` + +## DESCRIPTION +Get access token + +## EXAMPLES + +### Example 1 Get raw access token for ARM endpoint +```powershell +PS C:\> Get-AzAccessToken +``` + +Get access token of ResourceManager endpoint for current account + +### Example 2 Get raw access token for AAD graph endpoint +```powershell +PS C:\> Get-AzAccessToken -ResourceTypeName AadGraph +``` + +Get access token of AAD graph endpoint for current account + +### Example 3 Get raw access token for AAD graph endpoint +```powershell +PS C:\> Get-AzAccessToken -Resource "https://graph.windows.net/" +``` + +Get access token of AAD graph endpoint for current account + +## PARAMETERS + +### -DefaultProfile +The credentials, account, tenant, and subscription used for communication with Azure. + +```yaml +Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer +Parameter Sets: (All) +Aliases: AzContext, AzureRmContext, AzureCredential + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceTypeName +Optional resouce type name, supported values: AadGraph, AnalysisServices, Arm, Attestation, Batch, DataLake, KeyVault, OperationalInsights, ResourceManager, Synapse. Default value is Arm if not specified. + +```yaml +Type: System.String +Parameter Sets: KnownResourceTypeName +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceUrl +Resource url for that you're requesting token, e.g. 'http://graph.windows.net/'. + +```yaml +Type: System.String +Parameter Sets: ResourceUrl +Aliases: Resource, ResourceUri + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TenantId +Optional Tenant Id. Use tenant id of default context if not specified. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.String + +## NOTES + +## RELATED LINKS diff --git a/src/Accounts/Authenticators/MsalAccessToken.cs b/src/Accounts/Authenticators/MsalAccessToken.cs index b2480c7ca632..e66da95738dc 100644 --- a/src/Accounts/Authenticators/MsalAccessToken.cs +++ b/src/Accounts/Authenticators/MsalAccessToken.cs @@ -39,7 +39,7 @@ public class MsalAccessToken : IAccessToken public IDictionary ExtendedProperties { get; } = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private DateTimeOffset ExpiredOn { get; set; } + public DateTimeOffset ExpiresOn { get; set; } private readonly static TimeSpan ExpirationThreshold = TimeSpan.FromMinutes(5); @@ -53,7 +53,7 @@ public MsalAccessToken(TokenCredential tokenCredential, TokenRequestContext toke TokenCredential = tokenCredential; TokenRequestContext = tokenRequestContext; AccessToken = token; - ExpiredOn = expiresOn; + ExpiresOn = expiresOn; UserId = userId; TenantId = tenantId; HomeAccountId = homeAccountId; @@ -98,7 +98,7 @@ private void Renew() { var token = TokenCredential.GetToken(TokenRequestContext, default(CancellationToken)); AccessToken = token.Token; - ExpiredOn = token.ExpiresOn; + ExpiresOn = token.ExpiresOn; } } @@ -110,7 +110,7 @@ private bool IsNearExpiration() return true; } #endif - var timeUntilExpiration = ExpiredOn - DateTimeOffset.UtcNow; + var timeUntilExpiration = ExpiresOn - DateTimeOffset.UtcNow; return timeUntilExpiration < ExpirationThreshold; } }