/******************************************************************************* * 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.Collections.Generic; using System.Linq; using System.Management.Automation; using Amazon.Runtime; using System.Management.Automation.Host; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using Amazon.Util.Internal; using Amazon.Runtime.CredentialManagement; using System.Reflection; namespace Amazon.PowerShell.Common { /// /// /// Creates or updates the credential profile named 'default' and sets the profile, and optionally a region, /// as active in the current shell. The credential data to be stored in the 'default' profile can be provided /// from: /// /// A default region to assume when the default profile is active is also set using the -Region parameter, /// from a default region already set in the current shell or, if the cmdlet is executing on an EC2 instance, /// from the instance metadata. If a region setting cannot be determined from a parameter or the shell you are /// prompted to select one. /// /// /// Note that if run on an EC2 instance and you want to select a region other than the region containing the /// instance you should supply the -Region parameter so that the cmdlet does not inspect EC2 instance metadata /// to auto-discover the region. /// /// /// In all cases a profile named 'default' will be created or updated to contain the specified credential and /// region data. Note that if the credential source is another profile this cmdlet effectively copies the /// credential data from the source profile to the 'default' profile. /// /// /// When the cmdlet exits the active credentials can be accessed in the shell via a variable named $StoredAWSCredentials. /// The active region can be found in the variable $StoredAWSRegion. /// /// /// Note: For scripts written against earlier versions of this module this cmdlet can also be invoked with the alias Initialize-AWSDefaults. /// /// [Cmdlet("Initialize", "AWSDefaultConfiguration", DefaultParameterSetName = InitializeDefaultConfigurationCmdletParametersCmdlet.InstanceProfileSet)] [AWSCmdlet("Creates or updates the credential profile named 'default' using supplied keys, existing credentials or profile," + " or EC2 metadata. The profile and a default region is then set active in the current shell.", LegacyAlias = "Initialize-AWSDefaults")] [OutputType("None")] [AWSCmdletOutput("None", "This cmdlet does not generate any output.")] public class InitializeDefaultConfigurationCmdlet : InitializeDefaultConfigurationCmdletParametersCmdlet { public const string CredentialsPrompt = "Please choose one of the following stored credentials to use"; public const string RegionPrompt = "Please choose one of the following regions to use or specify one by system name"; public const string AccessKeyFieldName = "AWS Access Key"; public const string SecretKeyFieldName = "AWS Secret Key"; public const string RegionFieldName = "Region System Name"; const string CredentialsSourceMsg = "Credentials loaded from {0}."; internal static string CredentialsSourceMessage(AWSPSCredentials creds) { return string.Format(CredentialsSourceMsg, ServiceCmdlet.FormatCredentialSourceForDisplay(creds)); } protected override void ProcessRecord() { AWSPSCredentials awsPSCredentialsFromCommandLine; RegionEndpoint regionFromCommandLine; AWSPSCredentials awsPSCredentialsFromDefaultProfile; RegionEndpoint regionFromDefaultProfile; AWSPSCredentials awsPSCredentialsFromPromptingUser = null; RegionEndpoint regionFromPromptingUser = null; // get what was passed in on the command line, and what's already in defaults GetFromCommandLine(out awsPSCredentialsFromCommandLine, out regionFromCommandLine); GetFromDefaultProfile(out awsPSCredentialsFromDefaultProfile, out regionFromDefaultProfile); // if the command line and defaults don't provide us what we need then prompt the user if (awsPSCredentialsFromCommandLine == null && awsPSCredentialsFromDefaultProfile == null) awsPSCredentialsFromPromptingUser = PromptUserForCredentials(); if (regionFromCommandLine == null && regionFromDefaultProfile == null) regionFromPromptingUser = PromptUserForRegion(); // figure out what needs to be saved to disk and what needs to get put in session var awsPSCredentialsToPersist = awsPSCredentialsFromCommandLine == null ? awsPSCredentialsFromPromptingUser : awsPSCredentialsFromCommandLine; var awsPSCredentialsToPutInSession = awsPSCredentialsToPersist == null ? awsPSCredentialsFromDefaultProfile : awsPSCredentialsToPersist; var regionToPersist = regionFromCommandLine == null ? regionFromPromptingUser : regionFromCommandLine; var regionToPutInSession = regionToPersist == null ? regionFromDefaultProfile : regionToPersist; // save credentials and region to disk if necessary if (awsPSCredentialsToPersist != null || regionToPersist != null) { 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(awsPSCredentialsToPersist.Credentials); if (options != null) SettingsStore.RegisterProfile(options, SettingsStore.PSDefaultSettingName, ProfileLocation, regionToPersist); } 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, SettingsStore.PSDefaultSettingName, true); SettingsStore.RegisterProfile(new CredentialProfileOptions(), SettingsStore.PSDefaultSettingName, ProfileLocation, regionToPersist); } 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 the default profile. SettingsStore.RegisterProfile(GetCredentialProfileOptions(), SettingsStore.PSDefaultSettingName, ProfileLocation, regionToPersist); } } if (string.IsNullOrEmpty(ProfileLocation)) { WriteVerbose("Updated SDK profile store."); } else { WriteVerbose("Updated credential file at " + ProfileLocation); } WriteVerbose(string.Format("Default credentials and/or region have been stored to credentials profile '{0}' and set active for this shell.", SettingsStore.PSDefaultSettingName)); } string scope = MyInvocation.BoundParameters.ContainsKey("Scope") ? Scope.ToString() + ":" : ""; // put credentials and region in session this.SessionState.PSVariable.Set(scope + SessionKeys.AWSCredentialsVariableName, awsPSCredentialsToPutInSession); this.SessionState.PSVariable.Set(scope + SessionKeys.AWSRegionVariableName, regionToPutInSession.SystemName); } private void GetFromDefaultProfile(out AWSPSCredentials awsPSCredentialsFromDefaultProfile, out RegionEndpoint regionFromDefaultProfile) { CredentialProfile profile; if (SettingsStore.TryGetProfile(SettingsStore.PSDefaultSettingName, ProfileLocation, out profile)) { AWSCredentials defaultAWSCredentials; if (SettingsStore.TryGetAWSCredentials(SettingsStore.PSDefaultSettingName, ProfileLocation, out defaultAWSCredentials)) awsPSCredentialsFromDefaultProfile = new AWSPSCredentials(defaultAWSCredentials, SettingsStore.PSDefaultSettingName, CredentialsSource.Profile); else awsPSCredentialsFromDefaultProfile = null; regionFromDefaultProfile = profile.Region; } else { awsPSCredentialsFromDefaultProfile = null; regionFromDefaultProfile = null; } } private void GetFromCommandLine(out AWSPSCredentials awsPSCredentialsFromCommandLine, out RegionEndpoint regionFromCommandLine) { // Retrieve credentials if passed in (or from Instance Profile) if (this.TryGetCredentials(Host, out awsPSCredentialsFromCommandLine, SessionState)) { WriteVerbose(string.Format("{0}: Credentials for this shell were set from {1}", MyInvocation.MyCommand.Name, ServiceCmdlet.FormatCredentialSourceForDisplay(awsPSCredentialsFromCommandLine))); } RegionSource regionSource; if (this.TryGetRegion(true, out regionFromCommandLine, out regionSource, SessionState)) { WriteVerbose(string.Format("{0}: Region '{1}' was set for this shell from {2}", MyInvocation.MyCommand.Name, regionFromCommandLine.SystemName, ServiceCmdlet.FormatRegionSourceForDisplay(regionSource))); } } private RegionEndpoint PromptUserForRegion() { var choices = new Collection(); choices.Add(new ChoiceDescription("")); foreach (var reg in RegionEndpoint.EnumerableAllRegions) { choices.Add(new ChoiceDescription(reg.SystemName, reg.DisplayName)); } var choice = Host.UI.PromptForChoice("Specify region", RegionPrompt, choices, 0); if (choice != 0) { var chosenRegion = choices[choice]; return RegionEndpoint.GetBySystemName(chosenRegion.Label); } var descriptions = new Collection(); descriptions.Add(new FieldDescription(RegionFieldName)); var response = Host.UI.Prompt("Region", "Please enter a region to set", descriptions); var regionSystemName = response[RegionFieldName].BaseObject as string; if (string.IsNullOrEmpty(regionSystemName)) throw new ArgumentOutOfRangeException(RegionFieldName + " is not set"); return RegionEndpoint.GetBySystemName(regionSystemName); } private AWSPSCredentials PromptUserForCredentials() { var storedCredentials = SettingsStore.GetProfileInfo(ProfileLocation); if (storedCredentials.Any()) { // If there are stored credentials, ask user which ones to use, or enter new ones var choices = new Collection(); choices.Add(new ChoiceDescription("")); foreach (var profileInfo in storedCredentials) { // help message cannot be null var helpMessage = profileInfo.ProfileLocation == null ? profileInfo.StoreTypeName : profileInfo.ProfileLocation; choices.Add(new ChoiceDescription(profileInfo.ProfileName, helpMessage)); } var choice = Host.UI.PromptForChoice("Saved credentials found", CredentialsPrompt, choices, 0); if (choice != 0) { var chosenCredentials = choices[choice]; AWSCredentials awsCredentials; if (SettingsStore.TryGetAWSCredentials(chosenCredentials.Label, ProfileLocation, out awsCredentials)) { return new AWSPSCredentials(awsCredentials, chosenCredentials.Label, CredentialsSource.Profile); } } } var descriptions = new Collection(); descriptions.Add(new FieldDescription(AccessKeyFieldName)); descriptions.Add(new FieldDescription(SecretKeyFieldName)); var response = Host.UI.Prompt("Credentials", "Please enter your AWS Access and Secret Keys", descriptions); var accessKey = response[AccessKeyFieldName].BaseObject as string; var secretKey = response[SecretKeyFieldName].BaseObject as string; if (string.IsNullOrEmpty(accessKey)) throw new ArgumentOutOfRangeException(AccessKeyFieldName + " is not set"); if (string.IsNullOrEmpty(secretKey)) throw new ArgumentOutOfRangeException(SecretKeyFieldName + " is not set"); var credentials = new BasicAWSCredentials(accessKey, secretKey); // this set will be saved as our default name return new AWSPSCredentials(credentials, SettingsStore.PSDefaultSettingName, CredentialsSource.Profile); } } public class InitializeDefaultConfigurationCmdletParametersCmdlet : AWSCredentialsArgumentsFullCmdlet, IAWSCommonArgumentsFull { public const string InstanceProfileSet = "InstanceProfile"; public const string RegionOnlySet = "RegionOnly"; #region Parameter Region /// /// The system name of an AWS region or an AWSRegion instance. This governs /// the endpoint that will be used when calling service operations. Note that /// the AWS resources referenced in a call are usually region-specific. /// [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 210, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.BasicOrSessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 210, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AssumeRoleSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 210, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AWSCredentialsObjectSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 210, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.StoredProfileSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = true, Position = 210, ParameterSetName = InitializeDefaultConfigurationCmdletParametersCmdlet.RegionOnlySet)] [Alias(AWSRegionArgumentsCmdlet.RegionParameterAlias)] public object Region { 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. /// /// /// /// 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. /// [Alias("AWSProfilesLocation", "ProfilesLocation")] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 201, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.BasicOrSessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 201, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AssumeRoleSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 201, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.AWSCredentialsObjectSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 201, ParameterSetName = AWSCredentialsArgumentsFullCmdlet.StoredProfileSet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 201, ParameterSetName = InitializeDefaultConfigurationCmdletParametersCmdlet.RegionOnlySet)] [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false, Position = 201, ParameterSetName = InitializeDefaultConfigurationCmdletParametersCmdlet.InstanceProfileSet)] public override string ProfileLocation { get; set; } #endregion #region Parameter Scope /// /// /// This parameter allows to specify the scope of the shell variables set by this cmdlet. /// For details about variables scopes see https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes. /// /// [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false)] public VariableScope Scope { get; set; } #endregion } /// /// /// Clears the persisted credentials associated with the credential profile names 'default' and 'AWS PS Default', plus any /// credentials and region data currently set in the session's shell variables. To clear the stored default credentials only, /// use the -SkipShell parameter. To affect the current shell only, use the -SkipProfileStore parameter. /// /// /// Note: For scripts written against earlier versions of this module this cmdlet can also be invoked with the alias Clear-AWSDefaults. /// /// [Cmdlet("Clear", "AWSDefaultConfiguration")] [OutputType("None")] [AWSCmdletOutput("None", "This cmdlet does not generate any output.")] [AWSCmdlet("Clears the persisted credentials associated with the credential profile names 'default' and 'AWS PS Default', " + "plus any credentials and region data currently set in the session's shell variables. To clear the stored " + "default credentials only, use the -SkipShell parameter. To affect the current shell only, use the -SkipProfileStore parameter.", LegacyAlias = "Clear-AWSDefaults")] public class ClearDefaultConfigurationCmdlet : PSCmdlet { #region Parameter SkipShell /// /// If set, the cmdlet will not clear the current shell state. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter SkipShell { get; set; } #endregion #region Parameter SkipProfileStore /// /// If set, the cmdlet will not clear the 'default' and 'AWS PS Default' profiles held in the credentials store. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter SkipProfileStore { 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 for the /// 'default' and 'AWS PS Default' profiles. If the profiles are 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 shell variables 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 SkipShell is specified. /// /// [Parameter(ValueFromPipelineByPropertyName = true, Mandatory = false)] public VariableScope Scope { get; set; } #endregion protected override void ProcessRecord() { if (!SkipProfileStore.IsPresent) { WriteWarning("Please use the Remove-AWSCredentialProfile cmdlet to delete the default profile. This functionality will be removed from this cmdlet in a future release."); // Remove stored credentials SettingsStore.UnregisterProfile(SettingsStore.PSDefaultSettingName, ProfileLocation); // Remove stored legacy credentials SettingsStore.UnregisterProfile(SettingsStore.PSLegacyDefaultSettingName, ProfileLocation); } if (!SkipShell.IsPresent) { string scope = MyInvocation.BoundParameters.ContainsKey("Scope") ? Scope.ToString() + ":" : ""; // Clear credentials from shell this.SessionState.PSVariable.Set(scope + SessionKeys.AWSCredentialsVariableName, null); // Clear region from shell this.SessionState.PSVariable.Set(scope + SessionKeys.AWSRegionVariableName, null); } else if (MyInvocation.BoundParameters.ContainsKey("Scope")) { this.ThrowTerminatingError(new ErrorRecord( new ArgumentException("Parameters Scope and SkipShell cannot be used together."), "ArgumentException", ErrorCategory.InvalidArgument, this)); } } } /// /// Writes version and copyright information for the AWSPowerShell integration to the shell. If the ListServices switch is specified /// the services and their API versions supported by this module are also displayed. /// [Cmdlet("Get", "AWSPowerShellVersion")] [AWSCmdlet("Displays version and copyright information for the AWS Tools for Windows PowerShell to the shell. If the ListServiceVersionInfo switch is specified" + "the services and their API versions supported by this module are also displayed.")] [OutputType("String")] [AWSCmdletOutput("String", "Version information for the tools and optionally supported services.")] public class GetAWSPowerShellVersionCmdlet : PSCmdlet { #region Parameter ListServiceVersionInfo /// /// If specified the cmdlet also lists all supported AWS services and their versions. /// [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("ListServices")] public SwitchParameter ListServiceVersionInfo { get; set; } #endregion protected override void ProcessRecord() { base.ProcessRecord(); var powershellAssembly = TypeFactory.GetTypeInfo(typeof(BaseCmdlet)).Assembly; using (var sw = new StringWriter()) { var powershellInfo = FileVersionInfo.GetVersionInfo(powershellAssembly.Location); sw.WriteLine(); sw.WriteLine(powershellInfo.ProductName); sw.WriteLine("Version {0}", powershellInfo.FileVersion); sw.WriteLine(powershellInfo.LegalCopyright); var sdkAssembly = TypeFactory.GetTypeInfo(typeof(AWSCredentials)).Assembly; var sdkInfo = FileVersionInfo.GetVersionInfo(sdkAssembly.Location); sw.WriteLine(); sw.WriteLine(sdkInfo.ProductName); sw.WriteLine("Core Runtime Version {0}", sdkInfo.FileVersion); sw.WriteLine(sdkInfo.LegalCopyright); sw.WriteLine(); sw.WriteLine("Release notes: {0}", "https://github.com/aws/aws-tools-for-powershell/blob/master/CHANGELOG.md"); sw.WriteLine(); // recognise 3rd party libraries sw.WriteLine("This software includes third party software subject to the following copyrights:"); sw.WriteLine("- Logging from log4net, Apache License"); sw.WriteLine("[http://logging.apache.org/log4net/license.html]"); WriteObject(sw.ToString()); } if (ListServiceVersionInfo.IsPresent) { var services = GetAWSServiceCmdlet.GetServices(); foreach (var service in services.OrderBy(service => service.Name)) { var result = new PSObject(); result.Properties.Add(new PSNoteProperty("Service", service.Description)); result.Properties.Add(new PSNoteProperty("Noun Prefix", service.CmdletNounPrefix)); #if MODULAR result.Properties.Add(new PSNoteProperty("Module Name", service.ModuleName)); #endif result.Properties.Add(new PSNoteProperty("SDK Assembly Version", service.SDKAssemblyVersion)); WriteObject(result); } } } } }