/*******************************************************************************
* 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;
}
}
}