Skip to content

Commit

Permalink
Merge small improvements (#21)
Browse files Browse the repository at this point in the history
* Closes #19 - Essential changes for Linux/Core

* Add linux build

* Remove netcore manifest - it's autgenerated

* NO_CI Update readme

* NO_CI update readme

* Measure file size of template, not string length of body

* Improvements to event list formatting

* Comment build code

* Fix oversize template tests due to logic change

* NO_CI Bump module version.  Set release notes
  • Loading branch information
fireflycons authored Apr 14, 2019
1 parent bc42a56 commit 9f2b5e6
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 45 deletions.
47 changes: 46 additions & 1 deletion New-NetCoreManifest.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
<#
.SYNOPSIS
Generates a new Powershell Core module manifest from the Windows PowerShell one
.DESCRIPTION
Reads in PSCloudFormation.psd1 and writes out PSCloudFormation.netcore.psd1 making the following alterations
Adds
- CompatiblePSEditions = @('Core')
Modifies
GUID = a different GUID
RequiredModules = @('AWSPowerShell.netcore')
PowerShellVersion = "6.0"
PrivateData\PSData\ExternalModuleDependencies = @('AWSPowerShell.netcore')
#>
function RenderArray
{
<#
.SYNOPSIS
Render an array of strings to PowerShell code, i.e. @( ... )
.PARAMETER Arry
Array to render
.OUTPUTS
Array definition
#>
param
(
[Parameter(Position = 0)]
Expand All @@ -16,7 +40,18 @@ function RenderArray

function RenderHashTable
{
param
<#
.SYNOPSIS
Recursively render a hashtable to PowerShell code, i.e. @{ ... }
Not smart - only expects the types found in a manifest file.
.PARAMETER Arry
Hash to render
.OUTPUTS
Hash definition
#>
param
(
[hashtable]$Hash,
[int]$Level = 0
Expand Down Expand Up @@ -48,22 +83,32 @@ function RenderHashTable
$sb.ToString()
}

# Input manifest
$manifestFile = [IO.Path]::Combine($PSScriptRoot, "PSCloudFormation", "PSCloudFormation.psd1")

# Output manifest
$netCoreManifestFile = [IO.Path]::Combine($PSScriptRoot, "PSCloudFormation", "PSCloudFormation.netcore.psd1")

# GUID for netcore module
$netcoreGuid = '87c7f071-2c52-4fb7-9348-17de474650b8'

# Read manifest to hashtable
$manifest = Invoke-Expression "$(Get-Content -Raw $manifestFile)"

# Update values
$manifest['CompatiblePSEditions'] = @('Core')
$manifest['GUID'] = $netcoreGuid
$manifest['RequiredModules'] = @('AWSPowerShell.netcore')
$manifest['PowerShellVersion'] = '6.0'
$manifest['PrivateData']['PSData']['ExternalModuleDependencies'] = @('AWSPowerShell.netcore')

# Render hash back to powershell code
$netCoreManifest = RenderHashtable -Hash $manifest

# UTF8 without BOM encoding
$enc = New-Object System.Text.UTF8Encoding -ArgumentList $false

# Write new manifest
[IO.File]::WriteAllText($netCoreManifestFile, $netCoreManifest, $enc)

Write-Host "Generated $netCoreManifestFile"
4 changes: 2 additions & 2 deletions PSCloudFormation/PSCloudFormation.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = 'PSCloudFormation.psm1'

# Version number of this module.
ModuleVersion = '0.5.0'
ModuleVersion = '1.0.0'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down Expand Up @@ -110,7 +110,7 @@
# IconUri = ''

# ReleaseNotes of this module
ReleaseNotes = "Improve format of stack events.\nFix event timestamp bug - times are local not UTC.\nSupport JSON parameters.\nFix string formatting bug."
ReleaseNotes = "First major release. Now supports PowerShell Core/Linux!"

} # End of PSData hashtable

Expand Down
2 changes: 1 addition & 1 deletion PSCloudFormation/Private/Copy-OversizeTemplateToS3.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function Copy-OversizeTemplateToS3
}

# Measure the body size in bytes
if ([System.Text.ASCIIEncoding]::ASCII.GetByteCount($StackArguments['TemplateBody']) -lt 51200)
if ((Get-Item -Path $TemplateLocation).Length -lt 51200)
{
return
}
Expand Down
27 changes: 24 additions & 3 deletions PSCloudFormation/Private/Wait-PSCFNStack.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,29 @@ function Wait-PSCFNStack
'UPDATE_ROLLBACK_FAILED'
)

$anyFailed = $false
# Best guess how wide to make stack name column
$stackColumnWidth = $arns | Foreach-Object {
Get-CFNStack -StackName $_ @CredentialArguments
} |
Foreach-Object {
$stack = $_

# This stack
$stack.StackName.Length

# Nested stacks
Get-CFNStackResourceList -StackName $_.StackId @CredentialArguments |
Where-Object {
$_.ResourceType -ieq 'AWS::CloudFormation::Stack'
} |
Foreach-Object {
($stack.StackName + "-" + $_.LogicalResourceId + "-" + ("X" * 12)).Length
}
} |
Measure-Object -Maximum |
Select-Object -ExpandProperty Maximum

$anyFailed = $false
$writeHeaders = $true

while (($arns | Measure-Object).Count -gt 0)
Expand Down Expand Up @@ -77,7 +98,7 @@ function Wait-PSCFNStack
$arns = Compare-Object -ReferenceObject $arns -DifferenceObject $completedStackArns -PassThru
}

$ts = Write-StackEvents -StackArn $arns -EventsAfter $checkTime $CredentialArguments -WriteHeaders $writeHeaders
$ts = Write-StackEvents -StackArn $arns -EventsAfter $checkTime $CredentialArguments -WriteHeaders $writeHeaders -StackColumnWidth $stackColumnWidth
$writeHeaders = $false

if ($ts)
Expand All @@ -86,7 +107,7 @@ function Wait-PSCFNStack
}
}

Write-StackEvents -StackArn $StackArn -EventsAfter $checkTime $CredentialArguments -WriteHeaders $false
Write-StackEvents -StackArn $StackArn -EventsAfter $checkTime $CredentialArguments -WriteHeaders $false -StackColumnWidth $stackColumnWidth

# Output boolean - any final state containing ROLLBACK or FAILED indicates operation unsuccessful
-not $anyFailed
Expand Down
56 changes: 33 additions & 23 deletions PSCloudFormation/Private/Write-StackEvents.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ function Write-StackEvents
.PARAMETER WriteHeaders
If true, write column headers
.PARAMETER StackColumnWidth
Max width of stack name column
.OUTPUTS
Timestamp of most recent event
#>
Expand All @@ -28,7 +31,8 @@ function Write-StackEvents
[string[]]$StackArn,
[hashtable]$CredentialArguments,
[DateTime]$EventsAfter,
[bool]$WriteHeaders
[bool]$WriteHeaders,
[int]$StackColumnWidth
)

function Format-FixedWidthString
Expand Down Expand Up @@ -57,29 +61,35 @@ function Write-StackEvents
BodyOnly = -not $WriteHeaders
}

$events = Get-StackEvents -StackArn $StackArn -EventsAfter $EventsAfter -CredentialArguments $CredentialArguments
$events |
Sort-Object Timestamp |
ForEach-Object {
# Issue #15 - Create a new object of fixed width strings
[PSCustomObject][ordered]@{
TimeStamp = $ts = $_.Timestamp.ToString("HH:mm:ss")
StackName = Format-FixedWidthString -Text $_.StackName -Maxlen 40
'Logical ID' = Format-FixedWidthString -Text $_.LogicalResourceId -Maxlen 40
Status = Format-FixedWidthString -Text $_.ResourceStatus.Value -Maxlen 45
'Status Reason' = $(
if ([string]::IsNullOrEmpty($_.ResourceStatusReason))
{
"-"
}
else
{
$_.ResourceStatusReason
}
)
try {
$events = Get-StackEvents -StackArn $StackArn -EventsAfter $EventsAfter -CredentialArguments $CredentialArguments
$events |
Sort-Object Timestamp |
ForEach-Object {
# Issue #15 - Create a new object of fixed width strings
[PSCustomObject][ordered]@{
TimeStamp = $_.Timestamp.ToString("HH:mm:ss")
StackName = Format-FixedWidthString -Text $_.StackName -Maxlen $StackColumnWidth
'Logical ID' = Format-FixedWidthString -Text $_.LogicalResourceId -Maxlen 40
Status = Format-FixedWidthString -Text $_.ResourceStatus.Value -Maxlen 45
'Status Reason' = $(
if ([string]::IsNullOrEmpty($_.ResourceStatusReason))
{
"-"
}
else
{
$_.ResourceStatusReason
}
)
}
} |
Write-PSObject @writePSObjectArgs
}
} |
Write-PSObject @writePSObjectArgs
catch {
$_.ScriptStackTrace
throw
}

$events.Timestamp |
Measure-Object -Maximum |
Expand Down
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
[![Build status](https://ci.appveyor.com/api/projects/status/fgt7d0icj7emc6hl/branch/master?svg=true)](https://ci.appveyor.com/project/fireflycons/pscloudformation/branch/master)

# PSCloudFormation
|Branch|Status|
|------|------|
| master | [![Build status](https://ci.appveyor.com/api/projects/status/fgt7d0icj7emc6hl/branch/master?svg=true)](https://ci.appveyor.com/project/fireflycons/pscloudformation/branch/master)|
| dev | [![Build status](https://ci.appveyor.com/api/projects/status/fgt7d0icj7emc6hl/branch/dev?svg=true)](https://ci.appveyor.com/project/fireflycons/pscloudformation/branch/dev)|


## How to Install

The module is published on the PowerShell Gallery and can be installed by following the instructions there

### Windows PowerShell
https://www.powershellgallery.com/packages/PSCloudFormation

### PowerShell Core (Linux)
https://www.powershellgallery.com/packages/PSCloudFormation.netcore

Please note that currenly YAML templates are not supported in this version due to the fact that `powershell-yaml` does not work in Linux.
As soon as that gets sorted out, YAML support will be added to this project.

In the meantime, the guys at AWS have a tool that can convert templates between JSON and YAML formats [here](https://github.com/awslabs/aws-cfn-template-flip).

## What it isn't

It isn't intended as a replacement for the raw API `New-CFNStack` etc. You should continue to use those in automation scripts.
Expand All @@ -27,7 +39,7 @@ PowerShell builds a dynamic parameter set as soon as you give it an argument fro

With the cmdlets in this module, as soon as you tell them where the CloudFormation template is, they parse the template and extract all the parameters within and provide them as additional argments to the cmdlet, along with knowledge of whether they are required (no default), are constrained (AllowedValues, AllowedPattern), or typed (Number, `AWS::EC2::Subnet::Id` etc.) and perform pre-validation before submitting the stack update.

Additionally stack creation and update provide a dump of stack failure events to the console if errors occur.
If a cmdlet is given the `-Wait` parameter then all stack events are output to the console, including events from any nested stacks with status values in colour so you can quickly spot what is going on.

## Module Cmdlets

Expand All @@ -50,9 +62,11 @@ This module provides the following stack modification cmdlets

### Template Support

By default, only JSON templates are supported.
By default, only JSON templates are supported. YAML support in Windows PowerShell is enabled by installing `powershell-yaml` from the [PowerShell Gallery](https://www.powershellgallery.com/packages/powershell-yaml). For Linux/PowerShell Core, we are awaiting a build of `powershell-yaml` that works!

YAML support is enabled by installing `powershell-yaml` from the [PowerShell Gallery](https://www.powershellgallery.com/packages/powershell-yaml)
Oversize templates in your local file system (file size >= 51,200 bytes) are directly supported. They will be siliently uploaded to an S3 bucket which is created as necessary prior to processing. The bucket is named `cf-templates-pscloudformation-region-accountid` where
* `region` is the region you are building the stack in, e.g. `eu-west-1`.
* `accountid` is the numeric ID of the AWS account in which you are building the stack.

### Dynamic Template Parameter Arguments

Expand Down
10 changes: 3 additions & 7 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# See http://www.appveyor.com/docs/appveyor-yml for many more options

#Publish to PowerShell Gallery with this key
environment:
NuGetApiKey:
secure: ZwF65Rl156oMAenHhi8BAY0LySWhhjDvNNt91dpU7D5N88E8ToZyZHCziktv7sz3
APPVEYOR_SSH_KEY: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAnGF97nAjzT0pZn7JCrllP+2ao0k0hY9iAUH+kg8/gLrdEJhephXIynCjEuuSPeNB9XxSd1lwm8E80p55T0fYwuzP6LFX11GJVaIsnV4rH3SUFY//MuhILu7o2FxLINDT7Kjrc04VlNxQf6YDhYNeEcyJmK8JO/gVfZmfMuEPtbtbEw9v4v1qTJaotOlJ3gmaXg8kO580N5PYjKvqhirI1ygWe1HOyLny/k4xfKMCHlAvNxSIGXqJBU5OTwXvsXypgI0RQJw4DWv2XG3BSvJkvB/crdLBubZprWv+Q6RlyBzM8FysqXLxT289yGR7n57eq+NY3BSBLfdbYmSv15nFuQ==

image:
- Visual Studio 2017
- Ubuntu

# Does the whole process
test_script:
- ps: . ./build.ps1 -Task BuildAppVeyor

Expand All @@ -29,8 +28,5 @@ for:
skip_commits:
message: /NO_CI.*|updated readme.*|update readme.*s/

# No 'build' phase per-se
build: false

#Kick off the CI/CD pipeline
#test_script:
# - ps: . .\build.ps1
10 changes: 7 additions & 3 deletions tests/PSCloudFormation.Private.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ InModuleScope $ModuleName {
It 'Should not copy template to S3 if size less than 51200' {

$template = New-Object String -ArgumentList '*', 51119
$templatePath = [IO.Path]::Combine($TestDrive, "not-oversize.json")
[IO.File]::WriteAllText($templatePath, $template, (New-Object System.Text.ASCIIEncoding))

$stackArguments = @{
TemplateBody = $template
Expand All @@ -496,7 +498,7 @@ InModuleScope $ModuleName {
Mock -CommandName Write-S3Object -MockWith { }

$dateStamp = [DateTime]::UtcNow.ToString('yyyyMMddHHmmss')
Copy-OversizeTemplateToS3 -TemplateLocation test.json -CredentialArguments @{} -StackArguments $stackArguments
Copy-OversizeTemplateToS3 -TemplateLocation $templatePath -CredentialArguments @{} -StackArguments $stackArguments

$stackArguments.ContainsKey('TemplateBody') | Should Be $true
$StackArguments.ContainsKey('TemplateURL') | Should Be $false
Expand All @@ -509,6 +511,8 @@ InModuleScope $ModuleName {
It 'Should copy oversize template to S3' {

$template = New-Object String -ArgumentList '*', 51200
$templatePath = [IO.Path]::Combine($TestDrive, "oversize.json")
[IO.File]::WriteAllText($templatePath, $template, (New-Object System.Text.ASCIIEncoding))

$stackArguments = @{
TemplateBody = $template
Expand All @@ -531,11 +535,11 @@ InModuleScope $ModuleName {
Mock -CommandName Write-S3Object -MockWith { }

$dateStamp = [DateTime]::UtcNow.ToString('yyyyMMddHHmmss')
Copy-OversizeTemplateToS3 -TemplateLocation test.json -CredentialArguments @{} -StackArguments $stackArguments
Copy-OversizeTemplateToS3 -TemplateLocation $templatePath -CredentialArguments @{} -StackArguments $stackArguments

$stackArguments.ContainsKey('TemplateBody') | Should Be $false
$StackArguments.ContainsKey('TemplateURL') | Should Be $true
$stackArguments['TemplateURL'] | Should Be "https://s3.us-east-1.amazonaws.com/test-bucket/$($dateStamp)-test.json"
$stackArguments['TemplateURL'] | Should Be "https://s3.us-east-1.amazonaws.com/test-bucket/$($dateStamp)-oversize.json"
}
}
}
Expand Down

0 comments on commit 9f2b5e6

Please sign in to comment.