diff --git a/Pasm.ps1 b/Pasm.ps1 index 5c6e2cc..e7a6cec 100644 --- a/Pasm.ps1 +++ b/Pasm.ps1 @@ -55,7 +55,7 @@ end { CmdletsToExport = @() FunctionsToExport = (Get-ChildItem -LiteralPath (Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath 'src') -ChildPath 'Functions') -Filter '*.ps1').ForEach( { [path]::GetFileNameWithoutExtension($_.Name) } ) VariablesToExport = '*' - AliasesToExport = @('psmi', 'psmv', 'psmb', 'psmd', 'psma') + AliasesToExport = @('psmi', 'psmv', 'psmb', 'psmd', 'psma', 'psmc', 'psme') Tags = @('AWS', 'Linux', 'Windows') ProjectUri = 'https://github.com/{0}/{1}' -f $author, $moduleName LicenseUri = 'https://github.com/{0}/{1}/blob/main/LICENSE' -f $author, $moduleName diff --git a/README.md b/README.md index 3a52911..84c3206 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![build](https://github.com/nekrassov01/Pasm/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/nekrassov01/Pasm/actions/workflows/build.yml) [![release](https://github.com/nekrassov01/Pasm/actions/workflows/release.yml/badge.svg)](https://github.com/nekrassov01/Pasm/actions/workflows/release.yml) -Pasm is a PowerShell module for simple management of public IP address ranges provided by AWS. By simply following simple rules and creating YAML templates, you can keep up with IP range changes, deploy and synchronize resources. The currently supported resources are SecurityGroup, NetworkACL, and PrefixList. +Pasm is a PowerShell module for simple management of public IP address ranges provided by AWS. By simply following simple rules and creating YAML templates, you can keep up with IP range changes, deploy and synchronize resources. The currently supported resources are SecurityGroup, NetworkACL, and PrefixList. - [Pasm](#pasm) - [Compatible Editions](#compatible-editions) @@ -16,6 +16,7 @@ Pasm is a PowerShell module for simple management of public IP address ranges pr - [Generating Blueprint](#generating-blueprint) - [Deployment](#deployment) - [Clean up](#clean-up) + - [Export to CSV](#export-to-csv) - [Same Thing, Shorter](#same-thing-shorter) - [Aliases](#aliases) - [Sample Template (outline.yml)](#sample-template-outlineyml) @@ -33,7 +34,7 @@ Install the modules required to use Pasm. ```ps1 Install-Module -Name PowerShell-Yaml, AWS.Tools.Installer -Scope CurrentUser Install-AWSToolsModule -Name AWS.Tools.Common, AWS.Tools.EC2 -Scope CurrentUser -``` +``` Set the AWS credential. @@ -61,6 +62,7 @@ Pasm includes 6 functions. |Invoke-PasmDeployment|Read the blueprint and deploy resources.| |Invoke-PasmAutomation|Run the following in order: `Invoke-PasmValidation`, `Invoke-PasmBlueprint`, and `Invoke-PasmDeployment`| |Invoke-PasmCleanUp|Clean up the deployed resources.| +|Invoke-PasmExport|Based on the Yaml template, get the range of ip from [ip-ranges.json](https://ip-ranges.amazonaws.com/ip-ranges.json) and create a simple csv for external use.| ## Configuration Files @@ -182,17 +184,25 @@ ResourceType : NetworkAcl ResourceName : test-acl-01 ResourceId : acl-e1t2d3g4c5bryfhvn Detached : {subnet-q1z2x3w4e5cvrtbny, subnet-w1x2c3e4r5vbtynmu} -Skipped : +Skipped : Action : CleanUp ResourceType : PrefixList ResourceName : test-pl-01 ResourceId : pl-a1d2s3f4d5gfhgjhk Detached : {rtb-a1b2c3d4e5fghijkl, rtb-x1y2z3x4y5zxyzxyz, sg-1a2s3d4f5g6h7j890} -Skipped : +Skipped : Action : CleanUp ``` +### Export to CSV + +Output to simple CSV for external use. + +```ps1 +Invoke-PasmExport -FilePath output.csv +``` + ## Same Thing, Shorter `Invoke-PasmAutomation` runs the following in order: `Invoke-PasmValidation`, `Invoke-PasmBlueprint`, and `Invoke-PasmDeployment`. @@ -243,6 +253,11 @@ psma -file 'C:\Pasm\outline.yml' -out 'blueprint.yml' psmc -file 'C:\Pasm\blueprint.yml' ``` +```ps1 +# Invoke-PasmExport -FilePath 'C:\Pasm\outline.yml' -OutputFileName 'output.csv' +psme -file 'C:\Pasm\outline.yml' -out 'output.csv' +``` + ## Sample Template (outline.yml) 'outline.yml' will be deployed with comments. Please overwrite it according to your environment. @@ -270,7 +285,7 @@ Resource: # required FromPort: 80 # required - Range: 0-65535 ToPort: 80 # required - Range: 0-65535 - Id: 2 - ServiceKey: S3 + ServiceKey: S3 Region: - ap-northeast-1 IpFormat: @@ -278,7 +293,7 @@ Resource: # required Protocol: tcp FromPort: 443 ToPort: 443 - NetworkAcl: # not-required - One of the following must be present: 'SecurityGroup','NetworkAcl', 'PrefixList' + NetworkAcl: # not-required - One of the following must be present: 'SecurityGroup','NetworkAcl', 'PrefixList' - ResourceName: test-acl-01 # required VpcId: vpc-00000000000000000 # required MaxEntry: 20 # not-required - Range: 1-20 diff --git a/src/Functions/Invoke-PasmExport.ps1 b/src/Functions/Invoke-PasmExport.ps1 new file mode 100644 index 0000000..42e3e0f --- /dev/null +++ b/src/Functions/Invoke-PasmExport.ps1 @@ -0,0 +1,111 @@ +#Requires -Version 5.1 +using namespace System.IO +using namespace System.Collections.Generic + +function Invoke-PasmExport { + [CmdletBinding()] + [OutputType([System.IO.FileInfo[]])] + param ( + # Specify the path to the Yaml template. + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [Alias('file')] + [ValidateNotNullOrEmpty()] + [string[]]$FilePath = $($PWD, $('{0}.yml' -f [Pasm.Template.Name]::outline) -join [path]::DirectorySeparatorChar), + + # Specify the output file name. + [Parameter(Mandatory = $false)] + [Alias('out')] + [ValidateNotNullOrEmpty()] + [string[]]$OutputFileName = $('{0}.csv' -f [Pasm.Template.Name]::output) + #[string[]]$OutputFileName = 'output.csv' + ) + + begin { + try { + Set-StrictMode -Version Latest + + # Load helper functions + . $($PSScriptRoot, 'Helpers', 'Helpers.ps1' -join [path]::DirectorySeparatorChar) + + # Implicitly run the validator process. + Invoke-PasmValidation -FilePath $filePath -SkipIdValidation | Out-Null + + # Validation that the number of parameters match + if ($filePath.Length -ne $outputFileName.Length) { + throw [InvalidOperationException]::new('The length of the ''FilePath'' and the length of the ''OutputFileName'' must be the same.') + } + } + catch { + $PSCmdlet.ThrowTerminatingError($PSItem) + } + } + + process { + try { + $i = 0 + foreach ($file in $filePath) { + # Load outline file + $obj = Import-PasmFile -FilePath $file -Ordered + + # Create output file path + $outputFilePath = $([path]::GetDirectoryName($file), $OutputFileName[$i] -join [path]::DirectorySeparatorChar) + + # Create outer container + $container = [list[object]]::new() + + foreach ($resource in $([enum]::GetNames([Pasm.Parameter.Resource]))) { + if ($obj.Resource.Contains($resource)) { + foreach ($res in $obj.Resource.$resource) { + foreach ($rule in $res.Rules) { + foreach ($r in $(Get-PasmAWSIpRange $rule -Resource $resource)) { + $o = [ordered]@{} + $o.ServiceKey = $rule.ServiceKey + $o.IpPrefix = $r.IpPrefix + $o.IpFormat = $r.IpAddressFormat + $o.Region = $r.Region + $container.Add($o) + } + } + } + } + } + # Converts the object to csv format and writes it to a file + # If the file already exists, it will be overwritten + $container | Export-Csv -LiteralPath $outputFilePath -NoTypeInformation -Force + $i++ + $PSCmdlet.WriteObject([fileinfo]::new($outputFilePath)) + } + } + catch { + $PSCmdlet.ThrowTerminatingError($PSItem) + } + } + + end { + # Clean up processes, if any + } + + <# + .SYNOPSIS + Get the ip ranges from 'ip-ranges.json' as described in the Yaml template, and create a simple csv. + + .DESCRIPTION + Get the ip ranges from 'ip-ranges.json' as described in the Yaml template, and create a simple csv. + See the following source for details: https://github.com/nekrassov01/Pasm/blob/main/src/Functions/Invoke-PasmExport.ps1 + + .EXAMPLE + # Default input file path: ${PWD}/outline.yml, default output file name: 'output.csv' + Invoke-PasmExport + + .EXAMPLE + # Loading multiple files + Invoke-PasmExport -FilePath 'C:/Pasm/outline-sg.yml', 'C:/Pasm/outline-nacl.yml', 'C:/Pasm/outline-pl.yml' -OutputFileName 'blueprint-sg.csv', 'blueprint-nacl.csv', 'blueprint-pl.csv' + + .EXAMPLE + # Loading multiple files from pipeline + 'C:/Pasm/outline-sg.yml', 'C:/Pasm/outline-nacl.yml', 'C:/Pasm/outline-pl.yml' | Invoke-PasmBlueprint -OutputFileName 'blueprint-sg.csv', 'blueprint-nacl.csv', 'blueprint-pl.csv' + + .LINK + https://github.com/nekrassov01/Pasm + #> +} diff --git a/src/Functions/Invoke-PasmInitialize.ps1 b/src/Functions/Invoke-PasmInitialize.ps1 index 5e8f224..17647ef 100644 --- a/src/Functions/Invoke-PasmInitialize.ps1 +++ b/src/Functions/Invoke-PasmInitialize.ps1 @@ -134,7 +134,7 @@ Resource: # required if ($isExistsVpcId -or $isExistsSbnId) { $yaml = ConvertFrom-Yaml -Yaml $content -Ordered if ($isExistsVpcId) { - foreach ($resource in 'SecurityGroup', 'NetworkAcl', 'PrefixList') { + foreach ($resource in $([enum]::GetNames([Pasm.Parameter.Resource]))) { foreach ($r in $yaml.Resource.$resource) { if ($r.Contains('VpcId')) { $r.VpcId = $vpcId diff --git a/src/Functions/Invoke-PasmValidation.ps1 b/src/Functions/Invoke-PasmValidation.ps1 index 1cec9f3..e8f9dd3 100644 --- a/src/Functions/Invoke-PasmValidation.ps1 +++ b/src/Functions/Invoke-PasmValidation.ps1 @@ -9,7 +9,12 @@ function Invoke-PasmValidation { [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('file')] [ValidateNotNullOrEmpty()] - [string[]]$FilePath = $($PWD, $('{0}.yml' -f [Pasm.Template.Name]::outline) -join [path]::DirectorySeparatorChar) + [string[]]$FilePath = $($PWD, $('{0}.yml' -f [Pasm.Template.Name]::outline) -join [path]::DirectorySeparatorChar), + + # Skip validation of VpcId and SubnetId. + [Parameter(Mandatory = $false)] + [Alias('skip-id')] + [switch]$SkipIdValidation = $false ) begin { @@ -89,9 +94,11 @@ function Invoke-PasmValidation { Out-PasmLogLine -Message $('Validation passed: SecurityGroup ''{0}'' Rules' -f $sg.ResourceName) } - if ($sg.Contains('VpcId')) { - [void](Test-PasmVpcId -VpcId $sg.VpcId) - Out-PasmLogLine -Message $('Validation passed: SecurityGroup ''{0}'' VpcId' -f $sg.ResourceName) + if (!$PSBoundParameters.ContainsKey('SkipIdValidation')) { + if ($sg.Contains('VpcId')) { + [void](Test-PasmVpcId -VpcId $sg.VpcId) + Out-PasmLogLine -Message $('Validation passed: SecurityGroup ''{0}'' VpcId' -f $sg.ResourceName) + } } } } @@ -133,14 +140,16 @@ function Invoke-PasmValidation { Out-PasmLogLine -Message $('Validation passed: NetworkAcl ''{0}'' Rules' -f $nacl.ResourceName) } - if ($nacl.Contains('VpcId')) { - [void](Test-PasmVpcId -VpcId $nacl.VpcId) - Out-PasmLogLine -Message $('Validation passed: NetworkAcl ''{0}'' VpcId' -f $nacl.ResourceName) - } + if (!$PSBoundParameters.ContainsKey('SkipIdValidation')) { + if ($nacl.Contains('VpcId')) { + [void](Test-PasmVpcId -VpcId $nacl.VpcId) + Out-PasmLogLine -Message $('Validation passed: NetworkAcl ''{0}'' VpcId' -f $nacl.ResourceName) + } - if ($nacl.Contains('AssociationSubnetId')) { - [void](Test-PasmSubnetId -SubnetId $nacl.AssociationSubnetId) - Out-PasmLogLine -Message $('Validation passed: NetworkAcl ''{0}'' SubnetId' -f $nacl.ResourceName) + if ($nacl.Contains('AssociationSubnetId')) { + [void](Test-PasmSubnetId -SubnetId $nacl.AssociationSubnetId) + Out-PasmLogLine -Message $('Validation passed: NetworkAcl ''{0}'' SubnetId' -f $nacl.ResourceName) + } } } } @@ -170,9 +179,11 @@ function Invoke-PasmValidation { Out-PasmLogLine -Message $('Validation passed: PrefixList ''{0}'' Rules' -f $pl.ResourceName) } - if ($pl.Contains('VpcId')) { - [void](Test-PasmVpcId -VpcId $pl.VpcId) - Out-PasmLogLine -Message $('Validation passed: PrefixList ''{0}'' VpcId' -f $pl.ResourceName) + if (!$PSBoundParameters.ContainsKey('SkipIdValidation')) { + if ($pl.Contains('VpcId')) { + [void](Test-PasmVpcId -VpcId $pl.VpcId) + Out-PasmLogLine -Message $('Validation passed: PrefixList ''{0}'' VpcId' -f $pl.ResourceName) + } } } } diff --git a/src/Pasm.psd1 b/src/Pasm.psd1 index fafdc43..ceaf493 100644 --- a/src/Pasm.psd1 +++ b/src/Pasm.psd1 @@ -3,7 +3,7 @@ # # Generated by: nekrassov01 # -# Generated on: 2021/10/17 +# Generated on: 2021/11/28 # @{ @@ -12,7 +12,7 @@ RootModule = 'Pasm.psm1' # Version number of this module. -ModuleVersion = '1.2.0' +ModuleVersion = '1.3.8' # Supported PSEditions CompatiblePSEditions = 'Core', 'Desktop' @@ -51,8 +51,8 @@ ClrVersion = '4.0.0.0' # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module -RequiredModules = @('PowerShell-Yaml', - 'AWS.Tools.Common', +RequiredModules = @('PowerShell-Yaml', + 'AWS.Tools.Common', 'AWS.Tools.EC2') # Assemblies that must be loaded prior to importing this module @@ -71,8 +71,8 @@ RequiredModules = @('PowerShell-Yaml', # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'Invoke-PasmAutomation', 'Invoke-PasmBlueprint', - 'Invoke-PasmDeployment', 'Invoke-PasmInitialize', +FunctionsToExport = 'Invoke-PasmAutomation', 'Invoke-PasmBlueprint', 'Invoke-PasmCleanUp', + 'Invoke-PasmDeployment', 'Invoke-PasmExport', 'Invoke-PasmInitialize', 'Invoke-PasmValidation' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. @@ -82,7 +82,7 @@ CmdletsToExport = @() VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = 'psmi', 'psmv', 'psmb', 'psmd', 'psma' +AliasesToExport = 'psmi', 'psmv', 'psmb', 'psmd', 'psma', 'psmc', 'psme' # DSC resources to export from this module # DscResourcesToExport = @() @@ -111,7 +111,7 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = 'https://github.com/nekrassov01/Pasm/releases/tag/1.2.0' + ReleaseNotes = 'https://github.com/nekrassov01/Pasm/releases/tag/1.3.8' # Prerelease string of this module # Prerelease = '' @@ -133,3 +133,4 @@ PrivateData = @{ # DefaultCommandPrefix = '' } + diff --git a/src/Pasm.psm1 b/src/Pasm.psm1 index 1efd1b3..8ccb822 100644 --- a/src/Pasm.psm1 +++ b/src/Pasm.psm1 @@ -39,7 +39,7 @@ namespace ${moduleName}.RequiredParameter namespace ${moduleName}.Template { // Yaml template default file name - public enum Name { outline, blueprint } + public enum Name { outline, blueprint, output } } "@ @@ -53,6 +53,7 @@ $map = @{ 'psmd' = 'Invoke-PasmDeployment' 'psma' = 'Invoke-PasmAutomation' 'psmc' = 'Invoke-PasmCleanUp' + 'psme' = 'Invoke-PasmExport' } foreach ($m in $map.GetEnumerator()) { Set-Alias -Name $m.Key -Value $m.Value diff --git a/tests/Pasm.Tests.ps1 b/tests/Pasm.Tests.ps1 index 2145ff3..e85f5ab 100644 --- a/tests/Pasm.Tests.ps1 +++ b/tests/Pasm.Tests.ps1 @@ -189,6 +189,9 @@ InModuleScope 'Pasm' { It 'Validation' { Invoke-PasmValidation -FilePath $outlineFilePath | Should -BeTrue } + It 'Export' { + Invoke-PasmExport -FilePath $outlineFilePath | Should -BeTrue + } It 'Blueprint' { Invoke-PasmBlueprint -FilePath $outlineFilePath -OutputFileName $blueprintFileName | Should -BeTrue } @@ -217,6 +220,9 @@ InModuleScope 'Pasm' { It 'Validation' { Invoke-PasmValidation -FilePath $outlineFilePath | Should -BeTrue } + It 'Export' { + Invoke-PasmExport -FilePath $outlineFilePath | Should -BeTrue + } It 'Blueprint' { Invoke-PasmBlueprint -FilePath $outlineFilePath -OutputFileName $blueprintFileName | Should -BeTrue }