/*******************************************************************************
* 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.PowerShell.Common;
using Amazon.EC2.Model;
using Amazon.EC2.Util;
using Amazon.EC2;
using Amazon.Runtime;
namespace Amazon.PowerShell.Cmdlets.EC2
{
///
///
/// Outputs a collection of one or more Amazon Machine Images using either a set of supplied service-pack independent
/// 'logical' name pattern(s), or a set of custom name patterns. The set of service-pack independent logical names can
/// be viewed using the -ShowFilters switch.
///
///
/// If more than one name pattern is supplied (built-in or custom) then all available machine images that match the
/// pattern are output. If only a single name pattern is supplied and it corresponds to one of the built-in service-pack
/// independent names, then only the very latest machine image that matches is output. To see all versions of a machine
/// image that correspond to a built-in name, use the -AllAvailable switch.
///
///
/// This cmdlet is deprecated and will be removed in a future version. Use Get-SSMLatestEC2Image instead.
///
///
[Cmdlet("Get", "EC2ImageByName")]
[OutputType("string", "Amazon.EC2.Model.Image")]
[AWSCmdlet("Outputs a collection of one or more Amazon Machine Images using either a set of supplied service-pack independent"
+ "'logical' name pattern(s), or a set of custom name patterns. The set of service-pack independent logical names can "
+ "be viewed using the -ShowFilters switch.")]
[AWSCmdletOutput("Amazon.EC2.Model.Image",
"This cmdlet returns one or more Amazon Machine Image objects that correspond to a set of one or more name patterns. "
+ "If a single value is supplied for the -Name parameter, and it matches one of the built-in name patterns, only the latest machine image corresponding to the name "
+ "is output. To see all available versions of the image matching the name, use the -AllAvailable switch. "
+ "If more than one value is supplied to the -Name parameter, all images matching the set of name patterns are output (irrespective of "
+ "whether the names are built-in or custom). ",
"The service calls(s) made by the cmdlet to obtain the Image collection (type Amazon.EC2.Model.DescribeImagesResponse) are added to the cmdlet entry in the $AWSHistory stack."
)]
[AWSCmdletOutput("string", "If no parameters are supplied the cmdlet emits the built-in logical names that can be used with the -Name parameter.")]
#if MODULAR
[Obsolete("This cmdlet is deprecated and will be removed in a future version. Use Get-SSMLatestEC2Image from the AWS.Tools.SimpleSystemsManagement module instead.")]
#else
[Obsolete("This cmdlet is deprecated and will be removed in a future version. Use Get-SSMLatestEC2Image instead.")]
#endif
public class GetEC2ImageByNameCmdlet : AmazonEC2ClientCmdlet, IExecutor
{
#region Parameter Name
///
///
/// A collection of one or more name patterns to use as filters to select an image. If this parameter is
/// not specified, the set of built-in service-pack independent names are output.
///
///
/// If the name supplied is recognized as one of the built-in service pack independent 'logical' names,
/// it will be replaced internally by the corresponding pattern mapped by the logical name and used to
/// query EC2 to find the latest available image corresponding to that name pattern. Using independent names
/// means your script will continue to work even after AMIs are deprecated as new service packs are
/// released. Names containing service pack/RTM designations can be deprecated as machine images are
/// periodically refresh and eventually removed from the set of Amazon-published AMIs.
///
///
/// If more than one value is supplied for this parameter, all machine images matching the name
/// pattern are output, irrespective of whether the names supplied are from the built-in service-pack
/// independent set or are custom name patterns of your own making.
///
///
/// If a single value is supplied and it matches one of the built-in service-pack independent 'logical'
/// names then only the very latest machine image corresponding to that name is output. Use the -AllAvailable
/// switch to obtain the latest plus prior versions of the AMI.
///
///
[Alias("FilterNames","Names")]
[Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
public System.String[] Name { get; set; }
#endregion
#region Parameter ShowFilters
///
/// If set, the cmdlet emits the actual name pattern used to filter the machine images.
///
[Parameter(ValueFromPipelineByPropertyName = true)]
public SwitchParameter ShowFilters { get; set; }
#endregion
#region Parameter AllAvailable
///
///
/// Amazon Web Services periodically refreshes machine images and 'deprecates' the prior versions.
/// For a time it is possible for more than one image to correspond to a given logical filter.
///
///
/// If this switch is specified and the name filter(s) supplied to the cmdlet are recognized as
/// service pack independent 'logical' filters (i.e. the filter names shown if the -ShowFilters
/// switch is supplied) then the cmdlet will emit all of the images corresponding to the
/// filter. By default, the older images are suppressed and only the very latest image corresponding
/// to the filter is output.
///
///
/// If the supplied name filter(s) are not recognized (i.e. they are custom naming patterns of your
/// own construction) then this switch is ignored and all matching images are returned.
///
///
[Parameter(ValueFromPipelineByPropertyName = true)]
public SwitchParameter AllAvailable { get; set; }
#endregion
// maintains a redirect map of deprecated names and the current replacement
static readonly Dictionary DeprecatedNameSet = new Dictionary(StringComparer.OrdinalIgnoreCase);
static GetEC2ImageByNameCmdlet()
{
DeprecatedNameSet.Add("Windows_Server-2012-RTM-English-64Bit-SQL_2012_RTM_Express*", ImageUtilities.WINDOWS_2012_SQL_SERVER_EXPRESS_2012_KEY);
DeprecatedNameSet.Add("Windows_Server-2012-RTM-English-64Bit-SQL_2012_RTM_Standard*", ImageUtilities.WINDOWS_2012_SQL_SERVER_STANDARD_2012_KEY);
DeprecatedNameSet.Add("Windows_Server-2012-RTM-English-64Bit-SQL_2012_RTM_Web*", ImageUtilities.WINDOWS_2012_SQL_SERVER_WEB_2012_KEY);
DeprecatedNameSet.Add("Windows_Server-2008-R2_SP1-English-64Bit-SQL_2012_RTM_Express*", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_EXPRESS_2012_KEY);
DeprecatedNameSet.Add("Windows_Server-2008-R2_SP1-English-64Bit-SQL_2012_RTM_Standard*", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_STANDARD_2012_KEY);
DeprecatedNameSet.Add("Windows_Server-2008-R2_SP1-English-64Bit-SQL_2012_RTM_Web*", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_WEB_2012_KEY);
// these deprecations were added when 2008RTM was added and the existing 2008 image names remapped to 2008R2
DeprecatedNameSet.Add("WINDOWS_2008_BASE", ImageUtilities.WINDOWS_2008R2_BASE.DefinitionKey);
DeprecatedNameSet.Add("WINDOWS_2008_SQL_SERVER_EXPRESS_2012", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_EXPRESS_2012_KEY);
DeprecatedNameSet.Add("WINDOWS_2008_SQL_SERVER_STANDARD_2012", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_STANDARD_2012_KEY);
DeprecatedNameSet.Add("WINDOWS_2008_SQL_SERVER_WEB_2012", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_WEB_2012_KEY);
DeprecatedNameSet.Add("WINDOWS_2008_SQL_SERVER_EXPRESS_2008", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_EXPRESS_2008_KEY);
DeprecatedNameSet.Add("WINDOWS_2008_SQL_SERVER_STANDARD_2008", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_STANDARD_2008_KEY);
DeprecatedNameSet.Add("WINDOWS_2008_SQL_SERVER_WEB_2008", ImageUtilities.WINDOWS_2008R2_SQL_SERVER_WEB_2008_KEY);
}
protected override void ProcessRecord()
{
base.ProcessRecord();
var context = new CmdletContext
{
Names = this.Name,
ShowFilters = this.ShowFilters.IsPresent,
OutputAllAvailable = this.AllAvailable.IsPresent
};
ProcessOutput(Execute(context) as CmdletOutput);
}
#region IExecutor Members
public object Execute(ExecutorContext context)
{
var cmdletContext = context as CmdletContext;
if (cmdletContext == null)
throw new InvalidOperationException();
// Instantiate the client early so that any proxy settings configured via Set-AWSProxy will
// be picked up and available when the SDK downloads the key->filter metadata content for those
// customers that use a mandated and authenticated proxy.
var client = Client ?? CreateClient(_CurrentCredentials, _RegionEndpoint);
CmdletOutput output = null;
if (cmdletContext.Names == null || cmdletContext.Names.Length == 0)
{
IEnumerable data;
if (!cmdletContext.ShowFilters)
data = ImageUtilities.ImageKeys;
else
{
var filters = new List();
foreach (var k in ImageUtilities.ImageKeys)
{
var descriptor = LookupDescriptorByKey(k);
filters.Add(descriptor.NamePrefix);
}
data = filters;
}
output = new CmdletOutput
{
PipelineOutput = data
};
}
else
{
var customPatternUsed = false;
var patterns = new List();
foreach (var name in cmdletContext.Names)
{
// most preferable case - user gives us a service-pack/RTM designation-free 'logical' name,
// not affected by image deprecation
var imageDescriptor = LookupDescriptorByKey(name);
if (imageDescriptor != null)
{
patterns.Add(imageDescriptor.NamePrefix);
continue;
}
// if not an indepedent name, tell user they might want to update their script for
// better longevity
imageDescriptor = LookupDescriptorByName(name);
if (imageDescriptor != null)
{
var msg = string.Format("'{0}' may be deprecated at some future time.\r\nUse the version-independent replacement '{1}' to ensure your script always works.",
name,
imageDescriptor.DefinitionKey);
WriteWarning(msg);
patterns.Add(imageDescriptor.NamePrefix);
continue;
}
// if the user gave us a known deprecated image, remap it and again inform them
// they might want to update their script
if (DeprecatedNameSet.ContainsKey(name))
{
imageDescriptor = LookupDescriptorByKey(DeprecatedNameSet[name]);
var msg =
string.Format(
"'{0}' has been deprecated.\r\nUsing '{1}' instead.\r\nUse the version-independent replacement '{2}' to ensure your script always works.",
name,
imageDescriptor.NamePrefix,
DeprecatedNameSet[name]);
WriteWarning(msg);
patterns.Add(imageDescriptor.NamePrefix);
}
else
{
// final catch-all is to assume the name is for a non-stock ami, so use what we're given without
// complaint
patterns.Add(name);
customPatternUsed = true;
}
}
// use of multiple names, or any single custom name pattern, means we always output everything
if (patterns.Count > 1 || customPatternUsed)
cmdletContext.OutputAllAvailable = true;
// Decided to not use the helper inside Amazon.EC2.Util.ImageUtilities to do this, so I can capture
// the service request/response to the history stack and allow us to pass in multiple filter names.
var request = new DescribeImagesRequest
{
Owners = new List {"amazon"},
Filters = new List
{
new Filter {Name = "name", Values = patterns}
}
};
try
{
var response = CallAWSServiceOperation(client, request);
output = new CmdletOutput
{
ServiceResponse = response
};
// in previous versions of this cmdlet the output was always descending, regardless
// of whether the user gave us a custom or a built-in name pattern; to change
// it would potentially be a breaking change...
var sortedOutput = response.Images.OrderByDescending(x => x.Name);
if (!cmdletContext.OutputAllAvailable)
output.PipelineOutput = sortedOutput.First();
else
{
output.PipelineOutput = sortedOutput;
}
}
catch (Exception e)
{
output = new CmdletOutput { ErrorResponse = e };
}
}
return output;
}
ImageUtilities.ImageDescriptor LookupDescriptorByKey(string key)
{
#if DESKTOP
return ImageUtilities.DescriptorFromKey(key, Client);
#elif CORECLR
return ImageUtilities.DescriptorFromKeyAsync(key, Client).GetAwaiter().GetResult();
#else
#error "Unknown build edition"
#endif
}
ImageUtilities.ImageDescriptor LookupDescriptorByName(string name)
{
var keys = ImageUtilities.ImageKeys;
foreach (var k in keys)
{
var descriptor = LookupDescriptorByKey(k);
if (descriptor.NamePrefix.Equals(name, StringComparison.OrdinalIgnoreCase))
return descriptor;
}
return null;
}
public ExecutorContext CreateContext()
{
return new CmdletContext();
}
#endregion
#region AWS Service Operation Call
private Amazon.EC2.Model.DescribeImagesResponse CallAWSServiceOperation(IAmazonEC2 client, Amazon.EC2.Model.DescribeImagesRequest request)
{
Utils.Common.WriteVerboseEndpointMessage(this, client.Config, "Amazon EC2", "DescribeImages");
try
{
#if DESKTOP
return client.DescribeImages(request);
#elif CORECLR
return client.DescribeImagesAsync(request).GetAwaiter().GetResult();
#else
#error "Unknown build edition"
#endif
}
catch (AmazonServiceException exc)
{
var webException = exc.InnerException as System.Net.WebException;
if (webException != null)
{
throw new Exception(Utils.Common.FormatNameResolutionFailureMessage(client.Config, webException.Message), webException);
}
throw;
}
}
#endregion
internal class CmdletContext : ExecutorContext
{
public string[] Names { get; set; }
public bool ShowFilters { get; set; }
public bool OutputAllAvailable { get; set; }
}
}
}