Skip to content
This repository has been archived by the owner on Oct 21, 2023. It is now read-only.

Commit

Permalink
!deploy multiple fixes and enhancements
Browse files Browse the repository at this point in the history
## 2.11.0 - 2020-03-04

* [Issue #69](#69) + [PR #70](#70) - _Thanks, [@indented-automation](https://github.com/indented-automation)!!!_
  * Started `VaporShell.Core` class library to include with the module, first class being `TransformTagAttribute` to cleanly convert `Tags` parameter input to the appropriate format if not already.
  * Added Pester tests to confirm Tag transforms are working as expected.
* [Issue #68](#68) - _Thanks, [@indented-automation](https://github.com/indented-automation) and [@austoonz](https://github.com/austoonz)!!!_
  * Surfaced errors better on AWS SDK errors so the actual error is visible.
  * Added the `FallbackCredentialFactory` to better support running in environments where credentials files are not a practical option.
* Miscellaneous
  * Updated PseudoParameter list to current spec.
  * Added newer intrinsic functions `Add-FnCidr` and `Add-FnTransform`.
  • Loading branch information
scrthq authored Mar 4, 2020
2 parents 63ce3a1 + cd8a006 commit a87b9fa
Show file tree
Hide file tree
Showing 1,795 changed files with 29,460 additions and 4,525 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<!-- TOC -->

* [2.11.0 - 2020-03-04](#2110---2020-03-04)
* [2.10.1 - 2020-02-20](#2101---2020-02-20)
* [2.10.0 - 2020-02-18](#2100---2020-02-18)
* [2.9.5 - 2020-02-17](#295---2020-02-17)
Expand Down Expand Up @@ -54,6 +55,18 @@

<!-- /TOC -->

## 2.11.0 - 2020-03-04

* [Issue #69](https://github.com/scrthq/VaporShell/issues/69) + [PR #70](https://github.com/scrthq/VaporShell/pull/70) - _Thanks, [@indented-automation](https://github.com/indented-automation)!!!_
* Started `VaporShell.Core` class library to include with the module, first class being `TransformTagAttribute` to cleanly convert `Tags` parameter input to the appropriate format if not already.
* Added Pester tests to confirm Tag transforms are working as expected.
* [Issue #68](https://github.com/scrthq/VaporShell/issues/68) - _Thanks, [@indented-automation](https://github.com/indented-automation) and [@austoonz](https://github.com/austoonz)!!!_
* Surfaced errors better on AWS SDK errors so the actual error is visible.
* Added the `FallbackCredentialFactory` to better support running in environments where credentials files are not a practical option.
* Miscellaneous
* Updated PseudoParameter list to current spec.
* Added newer intrinsic functions `Add-FnCidr` and `Add-FnTransform`.

## 2.10.1 - 2020-02-20

* Miscellaneous
Expand Down
75 changes: 75 additions & 0 deletions Tests/Module Tests/TransformTagAttribute.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Describe 'TransformTagAttribute' {
BeforeAll {
function Test-TagBinding {
[CmdletBinding()]
param (
[VaporShell.Core.TransformTag()]
[object[]] $Tags
)

$Tags
}
}
Context 'Confirm TagTransformAttribute works as expected' {
$projectRoot = Resolve-Path "$PSScriptRoot\..\.."
$ModulePath = Resolve-Path "$projectRoot\BuildOutput\$($env:BHProjectName)"

Write-Verbose "Importing $($env:BHProjectName) module at [$ModulePath]"
Import-Module $ModulePath -Force -Verbose:$false

It 'Should return one or more tags when given <TestName>' -TestCases @(
@{
TestName = 'multiple hashtables'
InputData = @{ one = 1 }, @{ two = 2 }
ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }, [PSCustomObject]@{ Key = 'two'; Value = '2' }
}
@{
TestName = 'multiple PSObjects'
InputData = [PSCustomObject]@{ one = 1 }, [PSCustomObject]@{ two = 2 }
ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }, [PSCustomObject]@{ Key = 'two'; Value = '2' }
}
@{
TestName = 'a single hashtable with multiple keys'
InputData = [PSCustomobject]@{ one = 1; two = 2 }
ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }, [PSCustomObject]@{ Key = 'two'; Value = '2' }
}
@{
TestName = 'an existing VSTag object'
InputData = Add-VSTag -Key one -Value '1'
ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }
}
@{
TestName = 'a hashtable with Key and Value keys'
InputData = @{Key = 'Name'; Value = 'Harold'}
ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' }
}
@{
TestName = 'a hashtable with lowercase key and value keys'
InputData = @{ key = 'Name'; value = 'Harold'}
ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' }
}
@{
TestName = 'a hashtable with mixed case key and value keys'
InputData = @{ kEy = 'Name'; ValUE = 'Harold'}
ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' }
}
@{
TestName = 'a PSCustomObject key and value properties'
InputData = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' }
ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' }
}
@{
TestName = 'a PSCustomObject lowercase key and value properties'
InputData = [PSCustomObject]@{ key = 'Name'; value = 'Harold' }
ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' }
}
) {
param (
$InputData,
$ExpectedResult
)

Assert-Equivalent -Actual (Test-TagBinding -Tags $InputData) -Expected $ExpectedResult
}
}
}
129 changes: 129 additions & 0 deletions VaporShell.Core/VaporShell.Core.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;

namespace VaporShell.Core
{
public class TransformTagAttribute : ArgumentTransformationAttribute
{
private EngineIntrinsics engineIntrinsics;

private PSObject ConvertToTag(object key, object value)
{
PSObject tag = new PSObject();
tag.Members.Add(new PSNoteProperty("Key", key.ToString()));
tag.Members.Add(new PSNoteProperty("Value", value));
tag.TypeNames.Insert(0, "Vaporshell.Resource.Tag");

return tag;
}

private bool TryGetKey(IDictionary dictionary, string keyName, ref object KeyValue)
{
foreach (object key in dictionary.Keys)
{
if (key.ToString().ToLower() == keyName.ToLower())
{
KeyValue = dictionary[key];
return true;
}
}

return false;
}

private IEnumerable<PSObject> TransformHashtable(IDictionary inputData)
{
object keyName = null;
object value = null;
if (this.TryGetKey(inputData, "key", ref keyName) && this.TryGetKey(inputData, "value", ref value))
{
yield return this.ConvertToTag(keyName, value);
}
else
{
foreach (string key in inputData.Keys)
{
yield return this.ConvertToTag(key, inputData[key]);
}
}
}

private IEnumerable<PSObject> TransformPSObject(PSObject inputData)
{
var props = new List<string>();
foreach (var property in inputData.Properties)
{
props.Add(property.Name.ToLower());
}

if (props.Contains("key") && props.Contains("value"))
{
yield return this.ConvertToTag(inputData.Properties["Key"].Value, inputData.Properties["Value"].Value);
}
else
{
foreach (var property in inputData.Properties)
{
yield return this.ConvertToTag(property.Name, property.Value);
}
}
}

private IEnumerable<PSObject> TransformSingle(object inputData)
{
if (inputData is IDictionary)
{
foreach (PSObject tag in this.TransformHashtable(inputData as IDictionary))
{
yield return tag;
}
}
else if (inputData is PSObject)
{
PSObject psObject = inputData as PSObject;
if (psObject.TypeNames.Contains("Vaporshell.Resource.Tag"))
{
yield return psObject;
}
else if (psObject.TypeNames.Contains("System.Management.Automation.PSCustomObject"))
{
foreach (PSObject tag in this.TransformPSObject(psObject))
{
yield return tag;
}
}
}
}

private IEnumerable<PSObject> TransformData(object inputData)
{
if (inputData is Array)
{
foreach (object item in inputData as Array)
{
// This is returning an unenumerated array
foreach (PSObject tag in this.TransformSingle(item))
{
yield return tag;
}
}
}
else
{
foreach (PSObject tag in this.TransformSingle(inputData))
{
yield return tag;
}
}
}

public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
{
this.engineIntrinsics = engineIntrinsics;

return this.TransformData(inputData);
}
}
}
11 changes: 11 additions & 0 deletions VaporShell.Core/VaporShell.Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="PowerShellStandard.Library" Version="3.0.0" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions VaporShell.Core/VaporShell.Core.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29806.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VaporShell.Core", "VaporShell.Core.csproj", "{8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {115A389F-865B-4A5F-9761-0E606AB74BF5}
EndGlobalSection
EndGlobal
42 changes: 37 additions & 5 deletions VaporShell/Private/ProcessRequest3.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,50 @@ function ProcessRequest3 {
try {
$service = ($request.PSObject.TypeNames)[0].split('.')[1]
$sharedFile = New-Object Amazon.Runtime.CredentialManagement.SharedCredentialsFile -ErrorAction Stop
$endPoint = ($sharedFile.ListProfiles() | Where-Object {$_.Name -eq $ProfileName}).Region
$storedCreds = New-Object Amazon.Runtime.StoredProfileAWSCredentials $ProfileName -ErrorAction Stop
$matchedProfile = $sharedFile.ListProfiles() | Where-Object {$_.Name -eq $ProfileName}
if ($null -eq $matchedProfile) {
$creds = [Amazon.Runtime.FallbackCredentialsFactory]::GetCredentials()
$endPoint = if ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
[Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
}
else {
# Need to set a default if we can't resolve the region
Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
Start-Sleep -Seconds 5
[Amazon.RegionEndpoint]::USEast1
}
}
else {
$creds = New-Object Amazon.Runtime.StoredProfileAWSCredentials $ProfileName -ErrorAction Stop
$endPoint = if ($matchedProfile.Region) {
$matchedProfile.Region
}
elseif ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
[Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
}
else {
# Need to set a default if we can't resolve the region
Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
Start-Sleep -Seconds 5
[Amazon.RegionEndpoint]::USEast1
}
}
Write-Verbose "Building '$service' client in region '$($endPoint.DisplayName)' [$($endPoint.SystemName)]"
if ($endPoint) {
$client = New-Object "Amazon.$($service).Amazon$($service)Client" $storedCreds,$endPoint -ErrorAction Stop
$client = New-Object "Amazon.$($service).Amazon$($service)Client" $creds,$endPoint -ErrorAction Stop
}
else {
return (New-VSError -String "No region set for profile '$ProfileName'! Please run the following to set a region:`n`nSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>")
}
}
catch {
return (New-VSError -String "$($Error[0].Exception.Message)")
return (New-VSError -String "$($_.Exception.Message)")
}
Write-Verbose "Processing request:`n$($PSBoundParameters | Format-Table -AutoSize | Out-String)"
$i = 0
Expand Down Expand Up @@ -81,4 +113,4 @@ function ProcessRequest3 {
return $result
}
}
}
}
40 changes: 36 additions & 4 deletions VaporShell/Private/ProcessRequest4.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,50 @@ function ProcessRequest4 {
try {
$service = ($request.PSObject.TypeNames)[0].split('.')[1]
$sharedFile = New-Object Amazon.Runtime.CredentialManagement.SharedCredentialsFile -ErrorAction Stop
$endPoint = ($sharedFile.ListProfiles() | Where-Object {$_.Name -eq $ProfileName}).Region
$storedCreds = New-Object Amazon.Runtime.StoredProfileAWSCredentials $ProfileName -ErrorAction Stop
$matchedProfile = $sharedFile.ListProfiles() | Where-Object {$_.Name -eq $ProfileName}
if ($null -eq $matchedProfile) {
$creds = [Amazon.Runtime.FallbackCredentialsFactory]::GetCredentials()
$endPoint = if ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
[Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
}
else {
# Need to set a default if we can't resolve the region
Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
Start-Sleep -Seconds 5
[Amazon.RegionEndpoint]::USEast1
}
}
else {
$creds = New-Object Amazon.Runtime.StoredProfileAWSCredentials $ProfileName -ErrorAction Stop
$endPoint = if ($matchedProfile.Region) {
$matchedProfile.Region
}
elseif ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
[Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
}
else {
# Need to set a default if we can't resolve the region
Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
Start-Sleep -Seconds 5
[Amazon.RegionEndpoint]::USEast1
}
}
Write-Verbose "Building '$service' client in region '$($endPoint.DisplayName)' [$($endPoint.SystemName)]"
if ($endPoint) {
$client = New-Object "Amazon.$($service).Amazon$($service)Client" $storedCreds,$endPoint -ErrorAction Stop
$client = New-Object "Amazon.$($service).Amazon$($service)Client" $creds,$endPoint -ErrorAction Stop
}
else {
return (New-VSError -String "No region set for profile '$ProfileName'! Please run the following to set a region:`n`nSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>")
}
}
catch {
return (New-VSError -String "$($Error[0].Exception.Message)")
return (New-VSError -String "$($_.Exception.Message)")
}
if ($client | Get-Member -MemberType Method -Name "$Method*" | Where-Object {$_.Name -eq "$($Method)Async"}) {
$useAsync = $true
Expand Down
4 changes: 3 additions & 1 deletion VaporShell/Private/PseudoParams.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ AWS::NoValue
AWS::NotificationARNs
AWS::Region
AWS::StackName
AWS::Include
AWS::Include
AWS::Partition
AWS::URLSuffix
Loading

0 comments on commit a87b9fa

Please sign in to comment.