/******************************************************************************* * Copyright 2012-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. * This file 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. * ***************************************************************************** * * AWS Tools for Windows (TM) PowerShell (TM) * */ using System; using System.Linq; using System.Management.Automation; using System.Net; using System.Collections.Generic; using System.Management.Automation.Host; using System.Collections.ObjectModel; using Amazon.Runtime; using Amazon.Runtime.CredentialManagement; using Amazon.SecurityToken.SAML; namespace Amazon.PowerShell.Common { /// /// /// Creates and returns an AWSCredentials object /// /// /// Note: For scripts written against earlier versions of this module this cmdlet can also be invoked with the alias New-AWSCredentials. /// /// [Cmdlet("New", "AWSCredential")] [OutputType(typeof(AWSCredentials))] [AWSCmdlet("Returns a populated AWSCredentials instance.", LegacyAlias = "New-AWSCredentials")] [AWSCmdletOutput("AWSCredentials", "AWSCredentials instance containing AWS credentials data.")] public class NewCredentialCmdlet : AWSCredentialsArgumentsFullCmdlet { protected override void ProcessRecord() { AWSPSCredentials currentCredentials = null; if (this.TryGetCredentials(Host, out currentCredentials, SessionState)) { // used to be equivalent of write-host but got customer feedback that the inability to // suppress it interfered with console output (https://forums.aws.amazon.com/thread.jspa?threadID=211600&tstart=0). WriteVerbose(InitializeDefaultConfigurationCmdlet.CredentialsSourceMessage(currentCredentials)); } else { this.ThrowTerminatingError(new ErrorRecord(new ArgumentException("Cannot determine credentials from supplied parameters"), "ArgumentException", ErrorCategory.InvalidArgument, this)); } if (currentCredentials != null) WriteObject(currentCredentials.Credentials); } } /// /// /// This enumerable represents PowerShell variables scopes as described in https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes. /// /// public enum VariableScope { Global = -1, Local = -2, Script = -3, Private = -4 } /// /// /// Saves AWS credentials to persistent store (-StoreAs) or temporarily for the shell using shell variable $StoredAWSCredentials. /// /// /// Note: For scripts written against earlier versions of this module this cmdlet can also be invoked with the alias Set-AWSCredentials. /// /// [Cmdlet("Set", "AWSCredential")] [AWSCmdlet("Saves AWS credentials to persistent store (-StoreAs) or temporarily for the shell using shell variable $StoredAWSCredentials." + "Note that temporary session-based credentials cannot be saved to the persistent store.", LegacyAlias = "Set-AWSCredentials")] [OutputType("None")] [AWSCmdletOutput("None", "This cmdlet does not generate any output.")] public class SetCredentialCmdlet : SetCredentialsParametersCmdlet { protected override void ProcessRecord() { AWSPSCredentials currentCredentials = null; if (this.TryGetCredentials(Host, out currentCredentials, SessionState)) { WriteVerbose(InitializeDefaultConfigurationCmdlet.CredentialsSourceMessage(currentCredentials)); if (string.IsNullOrEmpty(StoreAs)) { SetUpIfFederatedCredentials(currentCredentials); string scope = MyInvocation.BoundParameters.ContainsKey("Scope") ? Scope.ToString() + ":" : ""; this.SessionState.PSVariable.Set(scope + SessionKeys.AWSCredentialsVariableName, currentCredentials); } else { if (MyInvocation.BoundParameters.ContainsKey("Scope")) { this.ThrowTerminatingError(new ErrorRecord( new ArgumentException("Parameters Scope and StoreAs cannot be used together."), "ArgumentException", ErrorCategory.InvalidArgument, this)); } if (string.Equals(AWSCredentialsArgumentsFullCmdlet.AWSCredentialsObjectSet, ParameterSetName, StringComparison.Ordinal)) { // We're storing from the -Credential parameter to a credentials file. // Only some types of AWSCredentials are supported. var options = CredentialProfileOptionsExtractor.ExtractProfileOptions(currentCredentials.Credentials); if (options != null) { SettingsStore.RegisterProfile(options, StoreAs, ProfileLocation, null); } } else { if (string.Equals(AWSCredentialsArgumentsFullCmdlet.StoredProfileSet, ParameterSetName, StringComparison.Ordinal)) { // We're copying from one profile to another. var chain = new CredentialProfileStoreChain(ProfileLocation); CredentialProfile profile; if (chain.TryGetProfile(ProfileName, out profile)) { profile.CredentialProfileStore.CopyProfile(ProfileName, StoreAs, true); } else // Parameters.TryGetCredentials has already tested for this but... this.ThrowTerminatingError(new ErrorRecord( new ArgumentException("Cannot determine credentials from supplied parameters"), "ArgumentException", ErrorCategory.InvalidArgument, this)); } else { // We're storing from individual command line values to a credentials file. SettingsStore.RegisterProfile(GetCredentialProfileOptions(), StoreAs, ProfileLocation, null); } } if (string.IsNullOrEmpty(ProfileLocation)) { WriteVerbose("Updated .NET credentials file."); } else { WriteVerbose("Updated shared credentials file at " + ProfileLocation); } } } else { this.ThrowTerminatingError(new ErrorRecord(new ArgumentException("Cannot determine credentials from supplied parameters"), "ArgumentException", ErrorCategory.InvalidArgument, this)); } } /// /// update the custom state we have applied to hold the host and any network credential /// the user has supplied to us as a shell default to fall back on if we get called to /// show the password dialog /// /// private void SetUpIfFederatedCredentials(AWSPSCredentials currentCredentials) { var callbackState = (currentCredentials.Credentials as FederatedAWSCredentials)?.Options.CustomCallbackState as SAMLCredentialCallbackState; #if DESKTOP if (callbackState == null) { #pragma warning disable CS0618 //A class was marked with the Obsolete attribute callbackState = (currentCredentials.Credentials as StoredProfileFederatedCredentials)?.CustomCallbackState as SAMLCredentialCallbackState; #pragma warning restore CS0618 //A class was marked with the Obsolete attribute } #endif if (callbackState != null) // is our callback that's attached { callbackState.ShellNetworkCredentialParameter = NetworkCredential; } } } public class SetCredentialsParametersCmdlet : AWSCredentialsArgumentsFullCmdlet { #region Parameter StoreAs /// /// /// The name to be used to identity the credentials in local storage. Use this with the -ProfileName parameter /// on cmdlets to load the stored credentials. /// /// [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.BasicOrSessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AssumeRoleSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AWSCredentialsObjectSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.StoredProfileSet)] public string StoreAs { get; set; } #endregion #region Parameter Scope /// /// /// When saving AWS credentials to the shell variable $StoredAWSCredentials, this parameter allows to specify the scope of the variable. /// For details about variables scopes see https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes. /// This parameter cannot be used when StoreAs is specified. /// /// [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.BasicOrSessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AssumeRoleSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AWSCredentialsObjectSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.StoredProfileSet)] public VariableScope Scope { get; set; } #endregion } /// /// /// Clears the set of AWS credentials currently set as default in the shell /// /// /// Note: For scripts written against earlier versions of this module this cmdlet can also be invoked with the alias Clear-AWSCredentials. /// /// [OutputType("None")] [AWSCmdlet("Clears the set of AWS credentials currently set as default in the shell or, if supplied with a name, deletes the set of credentials " + "associated with the supplied name from the local credentials store.", LegacyAlias = "Clear-AWSCredentials")] [AWSCmdletOutput("None", "This cmdlet does not generate any output.")] [Cmdlet("Clear", "AWSCredential")] public class ClearCredentialCmdlet : PSCmdlet { #region Parameter ProfileLocation /// /// /// Used to specify the name and location of the ini-format credential file (shared with /// the AWS CLI and other AWS SDKs) /// /// /// If this optional parameter is omitted this cmdlet will search the encrypted credential /// file used by the AWS SDK for .NET and AWS Toolkit for Visual Studio first. /// If the profile is not found then the cmdlet will search in the ini-format credential /// file at the default location: (user's home directory)\.aws\credentials. /// /// /// If this parameter is specified then this cmdlet will only search the ini-format credential /// file at the location given. /// /// /// As the current folder can vary in a shell or during script execution it is advised /// that you use specify a fully qualified path instead of a relative path. /// /// /// /// Note that the encrypted credential file is not supported on all platforms. /// It will be skipped when searching for profiles on Windows Nano Server, Mac, and Linux platforms. /// [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("AWSProfilesLocation", "ProfilesLocation")] public string ProfileLocation { get; set; } #endregion #region Parameter Scope /// /// /// This parameter allows to specify the scope of the variable $StoredAWSCredentials to clear. /// For details about variables scopes see https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes. /// This parameter cannot be used when ProfileName is specified. /// /// [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false)] public VariableScope Scope { get; set; } #endregion protected override void ProcessRecord() { string scope = MyInvocation.BoundParameters.ContainsKey("Scope") ? Scope.ToString() + ":" : ""; // clear credentials from shell this.SessionState.PSVariable.Set(scope + SessionKeys.AWSCredentialsVariableName, null); } } /// /// /// Returns an AWSCredentials object initialized with from either credentials currently set as default in the shell or saved and associated with the supplied name from the local credential store. /// Optionally the cmdlet can list the names, types, and locations of all sets of credentials held in local stores. /// /// /// Note: For scripts written against earlier versions of this module this cmdlet can also be invoked with the alias Get-AWSCredentials. /// /// [Cmdlet("Get", "AWSCredential", DefaultParameterSetName ="Shell")] [OutputType("Amazon.Runtime.AWSCredentials")] [OutputType("String")] [AWSCmdletOutput("Amazon.Runtime.AWSCredentials", "Object containing a set of AWS credentials.")] [AWSCmdletOutput("String", "The set of names associated with saved credentials in local stores.")] [AWSCmdletOutput("Amazon.Powershell.Common.ProfileInfo", "The set of names, types, and locations associated with saved credentials in local stores.")] [AWSCmdlet("Returns an AWSCredentials object initialized with from either credentials currently set as default in the shell or saved and " + "associated with the supplied name from a local credential store. Optionally the cmdlet can list the names, types, and locations " + "of all sets of credentials held in the store.", LegacyAlias = "Get-AWSCredentials")] public class GetCredentialCmdlet : PSCmdlet { #region Parameter ProfileName /// /// The name associated with the credentials in local storage /// [Parameter(Position = 1, ParameterSetName="SingleProfile", ValueFromPipelineByPropertyName = true)] [Alias("StoredCredentials")] public string ProfileName { get; set; } #endregion #region Parameter ProfileLocation /// /// /// Used to specify the name and location of the ini-format credential file (shared with /// the AWS CLI and other AWS SDKs) /// /// /// If this optional parameter is omitted this cmdlet will search the encrypted credential /// file used by the AWS SDK for .NET and AWS Toolkit for Visual Studio first. /// If the profile is not found then the cmdlet will search in the ini-format credential /// file at the default location: (user's home directory)\.aws\credentials. /// /// /// If this parameter is specified then this cmdlet will only search the ini-format credential /// file at the location given. /// /// /// As the current folder can vary in a shell or during script execution it is advised /// that you use specify a fully qualified path instead of a relative path. /// /// [Parameter(ParameterSetName = "SingleProfile", ValueFromPipelineByPropertyName = true)] [Parameter(ParameterSetName = "ListName", ValueFromPipelineByPropertyName = true)] [Parameter(ParameterSetName = "ListDetail", ValueFromPipelineByPropertyName = true)] [Alias("AWSProfilesLocation", "ProfilesLocation")] public string ProfileLocation { get; set; } #endregion #region Parameter ListProfile /// /// Lists the names of all CredentialProfiles saved in local storage /// [Parameter(ParameterSetName = "ListName", ValueFromPipelineByPropertyName = true)] [Alias("ListStoredCredentials", "ListProfiles")] public SwitchParameter ListProfile { get; set; } #endregion #region Parameter ListProfileDetail /// /// List the name, type, and location of all CredentialProfiles saved in local storage /// [Parameter(ParameterSetName = "ListDetail", ValueFromPipelineByPropertyName = true)] public SwitchParameter ListProfileDetail { get; set; } #endregion protected override void ProcessRecord() { if (ListProfile.IsPresent) { WriteWarning("The ListProfile switch is deprecated and will be removed from a future release. Please use ListProfileDetail instead."); WriteObject(SettingsStore.GetProfileInfo(ProfileLocation).Select(pi => pi.ProfileName), true); return; } else if (ListProfileDetail.IsPresent) { WriteObject(SettingsStore.GetProfileInfo(ProfileLocation), true); return; } if (string.IsNullOrEmpty(ProfileName)) { var creds = this.SessionState.PSVariable.Get(SessionKeys.AWSCredentialsVariableName); if (creds != null && creds.Value != null && creds.Value is AWSPSCredentials) WriteObject((creds.Value as AWSPSCredentials).Credentials); } else { CredentialProfile profile; if (SettingsStore.TryGetProfile(ProfileName, ProfileLocation, out profile)) { WriteObject(profile.GetAWSCredentials(profile.CredentialProfileStore)); } } } } /// /// Creates or updates an endpoint settings definition for use with SAML role profiles. The name of /// the endpoint settings is used with the Set-AWSSamlRoleProfile and Set-AWSCredentials cmdlets to associate one or more /// role profiles to a shared endpoint definition. /// [Cmdlet("Set", "AWSSamlEndpoint")] [OutputType(typeof(string))] [AWSCmdletOutput("System.String", "The cmdlet returns the name assigned to the endpoint settings to the pipeline.")] [AWSCmdlet("Creates or updates an endpoint settings definition for use with SAML role profiles.")] public class SetSamlEndpointProfileCmdlet : BaseCmdlet { #region Parameter Endpoint /// /// The endpoint to be used when authenticating users prior to requesting temporary role- /// based AWS credentials. The full endpoint of the identity provider must be specified and /// it must be a HTTPS-scheme URL. /// [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public Uri Endpoint { get; set; } #endregion #region Parameter StoreAs /// /// The user-defined name to assign to the endpoint settings. This name will be used when creating or /// accessing role profiles with the Set-AWSSamlRoleProfile cmdlet to set up and use role-based /// credential profiles that use the endpoint to authenticate the user. /// [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("EndpointName")] public string StoreAs { get; set; } #endregion #region Parameter AuthenticationType /// /// The authentication type (or protocol type) used when communicating with the endpoint. /// If not configured for an endpoint 'Kerberos' is assumed. /// [Parameter(ValueFromPipelineByPropertyName = true)] [ValidateSet("NTLM", "Digest", "Kerberos", "Negotiate")] public string AuthenticationType { get; set; } #endregion protected override void ProcessRecord() { base.ProcessRecord(); var samlEndpointManager = new SAMLEndpointManager(); SAMLEndpoint samlEndpoint; if (ParameterWasBound("AuthenticationType")) { var authenticationType = (SAMLAuthenticationType)(Enum.Parse(typeof(SAMLAuthenticationType), AuthenticationType)); samlEndpoint = new SAMLEndpoint(StoreAs, Endpoint, authenticationType); } else { samlEndpoint = new SAMLEndpoint(StoreAs, Endpoint); } samlEndpointManager.RegisterEndpoint(samlEndpoint); WriteObject(StoreAs); } } /// /// /// Creates or updates role profiles for use with a SAML federated identity provider to obtain temporary /// AWS credentials for roles the user is authorized to assume. The endpoint for authentication should have /// been configured previously using Set-AWSSamlEndpoint. Once created the role profiles can be used to obtain /// time-limited temporary AWS credentials by specifying the name of the role profile to the -ProfileName /// parameter of the Set-AWSCredentials cmdlet or any cmdlet that makes calls to AWS service operations. /// ///

/// /// User authentication is not performed until AWS credentials are required, i.e. just prior to a service /// operation call. Additionally if the credentials expire then the tools will automatically attempt to /// re-authenticate the user to obtain fresh credentials. When a role profile is configured to use the /// default logged-in user identity then this process happens silently. If a role profile is configured /// to use an alternate identity (by specifying the -NetworkCredential parameter) the user is prompted to /// re-enter their credentials prior to re-authentication. /// ///
[Cmdlet("Set", "AWSSamlRoleProfile", DefaultParameterSetName = StoreOneRoleParameterSet)] [AWSCmdlet("Creates or updates one or more role profiles for use with authentication against a SAML-based federated identity provider to obtain temporary role-based AWS credentials.")] [OutputType(typeof(string))] [AWSCmdletOutput("System.String", "This cmdlet returns the name of the role profile to the pipeline. If the -StoreAllRoles switch is used the names of all created or updated profiles are output.")] public class SetSamlRoleProfileCmdlet : BaseCmdlet { public const string RolePrompt = "Select the role to be assumed when this profile is active"; private const string StoreAllRolesParameterSet = "StoreAllRoles"; private const string StoreOneRoleParameterSet = "StoreOneRole"; #region Parameter EndpointName /// /// The name assigned to the endpoint definition that was previously registered using Set-AWSSamlEndpoint. /// The endpoint definition contains the URL of the endpoint to be used to authenticate users prior to /// vending temporary AWS credentials. /// [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string EndpointName { get; set; } #endregion #region Parameter PrincipalARN /// /// /// The Amazon Resource Name (ARN) of the principal holding the role to be assumed when credentials are /// requested following successful authentication. If specified the RoleARN parameter must also /// be specified. /// ///

/// /// If neither of the PrincipalARN and RoleARN parameters are supplied and the user is authorized /// to assume multiple roles the cmdlet will prompt to select the role that should be referenced /// by the profile. The user is also prompted if ARNs are specified but cannot be found in the data /// returned on successful authentication. /// ///
[Parameter(ParameterSetName = StoreOneRoleParameterSet, ValueFromPipelineByPropertyName = true)] public string PrincipalARN { get; set; } #endregion #region Parameter RoleARN /// /// /// The Amazon Resource Name (ARN) of the role to be assumed when credentials are requested following /// successful authentication. If specified the PrincipalARN parameter must also be specified. /// ///

/// /// If neither of the PrincipalARN and RoleARN parameters are supplied and the user is authorized /// to assume multiple roles the cmdlet will prompt to select the role that should be referenced /// by the profile. The user is also prompted if ARNs are specified but cannot be found in the data /// returned on successful authentication. /// ///
[Parameter(ParameterSetName = StoreOneRoleParameterSet, ValueFromPipelineByPropertyName = true)] public string RoleARN { get; set; } #endregion #region Parameter NetworkCredential /// /// /// Optional. Supply a value only if an identity different to the user's default Windows identity /// should be used during authentication. /// ///

/// /// If an alternate credential is specified then when the tools need to re-authenticate the user /// to obtain fresh credentials following expiry the user is prompted to re-enter the password /// for the user account before re-authentication can be performed. When the default user identity /// is configured for use (-NetworkCredential not specified) re-authentication occurs silently. /// ///
[Parameter(ValueFromPipelineByPropertyName = true)] [Alias("Credential", "UserCredential")] public PSCredential NetworkCredential { get; set; } #endregion #region Parameter StoreAs /// /// The name to associate with the role data. This name will be used with the -ProfileName parameter /// to Set-AWSCredentials cmdlet and AWS service cmdlets to load the profile and obtain temporary /// AWS credentials based on the role and other data held in the profile. /// [Parameter(Mandatory = true, ParameterSetName = StoreOneRoleParameterSet, ValueFromPipelineByPropertyName = true)] public string StoreAs { get; set; } #endregion #region Parameter StoreAllRoles /// /// If set all roles available to the user are evaluated following authentication and one /// role profile per role will be created. The name of each role will be used for each /// corresponding profile that is created. /// [Parameter(Mandatory = true, ParameterSetName = StoreAllRolesParameterSet, ValueFromPipelineByPropertyName = true)] public SwitchParameter StoreAllRoles { get; set; } #endregion #region Parameter STSEndpointRegion /// /// /// Specifies the region to be used when making calls to STS to obtain temporary credentials /// after successful authentication. /// /// /// This parameter is only needed in regions where a specific regional endpoint for STS must /// be used (eg cn-north-1). In all regions where the global sts.amazonaws.com endpoint can be /// used this parameter should not be specified. /// /// [Parameter(ValueFromPipelineByPropertyName = true)] public string STSEndpointRegion { get; set; } #endregion protected override void ProcessRecord() { try { string selectedRoleARN = null; NetworkCredential networkCredential = null; if (this.NetworkCredential != null) networkCredential = this.NetworkCredential.GetNetworkCredential(); var havePrincipal = ParameterWasBound("PrincipalARN"); var haveRole = ParameterWasBound("RoleARN"); if (havePrincipal != haveRole) ThrowExecutionError("RoleARN must be specified with PrincipalARN.", this); if (havePrincipal && haveRole) selectedRoleARN = string.Concat(PrincipalARN, ",", RoleARN); WriteVerbose("Authenticating with endpoint to verify role data..."); var samlEndpoint = (new SAMLEndpointManager()).GetEndpoint(EndpointName); var authenticationController = new SAMLAuthenticationController(); var samlAssertion = authenticationController.GetSAMLAssertion(samlEndpoint.EndpointUri.ToString(), networkCredential, samlEndpoint.AuthenticationType.ToString()); RegionEndpoint stsRegionEndpoint = null; if (!string.IsNullOrEmpty(STSEndpointRegion)) stsRegionEndpoint = RegionEndpoint.GetBySystemName(STSEndpointRegion); if (StoreAllRoles) { string domainUser = null; if (networkCredential != null) { // some credentials are entered in email format, so do not assume a domain // was present if (string.IsNullOrEmpty(networkCredential.Domain)) domainUser = networkCredential.UserName; else domainUser = string.Format(@"{0}\{1}", networkCredential.Domain, networkCredential.UserName); } var availableRoles = samlAssertion.RoleSet; foreach (var roleName in availableRoles.Keys) { selectedRoleARN = availableRoles[roleName]; WriteVerbose(string.Format("Saving role '{0}' to profile '{1}'.", selectedRoleARN, roleName)); var options = new CredentialProfileOptions() { EndpointName = EndpointName, RoleArn = selectedRoleARN, UserIdentity = domainUser }; SettingsStore.RegisterProfile(options, roleName, null, stsRegionEndpoint); WriteObject(roleName); } } else { var profileName = SelectAndStoreProfileForRole(samlAssertion.RoleSet, selectedRoleARN, networkCredential, stsRegionEndpoint); WriteObject(profileName); } } catch (Exception e) { this.ThrowTerminatingError(new ErrorRecord(new ArgumentException("Unable to set credentials: " + e.Message, e), "ArgumentException", ErrorCategory.InvalidArgument, this)); } } private PSCredential RequestPasswordFromUser(string domainUser) { return Host.UI.PromptForCredential("Password Required", "Please enter the password for " + domainUser, domainUser, ""); } private bool TestPreselectedRoleAvailable(string targetPrincipalAndRoleARNs, ICollection roleARNs) { foreach (var r in roleARNs) { if (r.Equals(targetPrincipalAndRoleARNs, StringComparison.OrdinalIgnoreCase)) return true; } WriteVerbose(string.Format("The specified principal and role ARNs, {0}, could not be found in the SAML response.", targetPrincipalAndRoleARNs)); return false; } private string SelectAndStoreProfileForRole(IDictionary availableRoles, string preselectedRoleARN, NetworkCredential networkCredential, RegionEndpoint stsEndpointRegion) { var selectedRoleARN = preselectedRoleARN; var roleSelectionNeeded = true; if (!string.IsNullOrEmpty(selectedRoleARN)) roleSelectionNeeded = !TestPreselectedRoleAvailable(selectedRoleARN, availableRoles.Values); if (roleSelectionNeeded) { // if only one role, preselect if (availableRoles.Count == 1) { selectedRoleARN = availableRoles.Values.First(); WriteVerbose(string.Format("Only one role available, pre-selecting role ARN {0}", preselectedRoleARN)); } else { var choices = new Collection(); // PowerShell labelling doesn't work if the shortcut is not in the label text (it // adds it as extra text, which looks odd). Trying to select characters in the names // can also be funky so the simplest approach is to manually prefix each role with an // index number that is the shortcut. We used 1..9 on initial launch of this feature, then // found a user with more than 10 roles so switched to alpha. var shortcut = 'A'; foreach (var r in availableRoles.Keys) { string label = null; if (shortcut <= 'Z') label = string.Concat("&", shortcut++, " - ", r); else label = r; choices.Add(new ChoiceDescription(label, availableRoles[r])); } var choice = Host.UI.PromptForChoice("Select Role", RolePrompt, choices, 0); selectedRoleARN = choices[choice].HelpMessage; } } if (string.IsNullOrEmpty(selectedRoleARN)) ThrowExecutionError("A role is required before the profile can be stored.", this); WriteVerbose(string.Format("Saving to profile '{0}'.\r\nUse 'Set-AWSCredentials -ProfileName {0}' to load this profile and obtain temporary AWS credentials.", StoreAs)); string domainUser = null; if (networkCredential != null) { // user identity can be expressed in domain\user or email@domain formats - so don't auto-format // to domain\user unless a domain is actually present (if we do, profile ends up with \user@domain // and the user needs to strip it out each time we demand the password) domainUser = string.IsNullOrEmpty(networkCredential.Domain) ? networkCredential.UserName : string.Concat(networkCredential.Domain, "\\", networkCredential.UserName); } var options = new CredentialProfileOptions() { EndpointName = EndpointName, RoleArn = selectedRoleARN, UserIdentity = domainUser }; SettingsStore.RegisterProfile(options, StoreAs, null, stsEndpointRegion); return StoreAs; } } }