Functions/Helpers/Helpers.ps1
#Requires -Version 5.1 using namespace System.IO using namespace System.Management.Automation using namespace System.Collections.Generic using namespace Amazon.Util using namespace Amazon.EC2.Model # Convert an array of key names to a string (Helper for helper functions) function Get-ArrayToString { param ( [string[]]$Array ) if (1 -eq @($array).Length) { return $('''{0}''' -f $array) } else { return $('''{0}'', and ''{1}''' -f ($array[0..($array.Length - 2)] -join ''', '''), $array[-1]) } } # Create log message line function Out-PasmLogLine { param ( [string]$Message, [switch]$TimePrefix, [string]$TimePrefixStyle = '{0} | {1}' ) if ($PSBoundParameters.ContainsKey('TimePrefix')) { $message = $timePrefixStyle -f $(Get-Date), $message } return $message } # Import Yaml template file function Import-PasmFile { param ( [string]$FilePath, [switch]$Ordered ) if (!(Test-Path -LiteralPath $filePath)) { throw [FileNotFoundException]::new($('Yaml template ''{0}'' does not exist.' -f [path]::GetFileName($filePath))) } try { ConvertFrom-Yaml -Yaml $(Get-Content -LiteralPath $filePath -Raw) -Ordered:$ordered } catch { throw [FormatException]::new($('Yaml template could not be loaded because invalid format detected in ''{0}''.' -f [path]::GetFileName($filePath))) } } # Yaml template validation: required keys function Test-PasmRequiredKey { param ( [object]$InputObject, [string]$Enum = 'Pasm.RequiredParameter.Parent' ) $member = [enum]::GetNames($enum) $label = if ($enum -eq 'Pasm.RequiredParameter.Parent') { 'top-level' } else { '''{0}''' -f $enum.Split('.')[-1] } foreach ($obj in $inputObject) { foreach ($m in $member) { if ($m -notin $obj.Keys) { throw [FormatException]::new($('The required key ''{0}'' is missing in {1} section.' -f $m, $label)) } } } } # Yaml template validation: allowed keys function Test-PasmInvalidKey { param ( [object]$InputObject, [string]$Enum = 'Pasm.Parameter.Parent' ) $member = [enum]::GetNames($enum) $label = if ($enum -eq 'Pasm.Parameter.Parent') { 'top-level' } else { '''{0}''' -f $enum.Split('.')[-1] } foreach ($obj in $inputObject) { foreach ($o in $obj.GetEnumerator()) { if ($o.Key -notin $member) { throw [FormatException]::new($('Invalid parameter key ''{0}'' detected. Allowed in {1} section: {2}.' -f $o.Key, $label, $(Get-ArrayToString -Array $member))) } } } } # Yaml template validation: at least one key is present function Test-PasmEmptyKey { param ( [object]$InputObject, [string]$Enum = 'Pasm.Parameter.Parent' ) $member = [enum]::GetNames($enum) foreach ($m in $member) { if ($inputObject.Contains($m)) { if ($null -eq $inputObject.$m) { throw [FormatException]::new($('Empty section exists: ''{0}''' -f $m)) } } } } # Yaml template validation: only one value function Test-PasmScalarValue { param ( [object]$InputObject, [string[]]$Key ) foreach ($k in $key) { foreach ($obj in $inputObject) { if ($obj.Contains($k)) { if (1 -ne @($obj.$k).Length) { throw [FormatException]::new($('Only one value can be specified for ''{0}''.' -f $k)) } } } } } # Yaml template validation: allowed values function Test-PasmInvalidValue { param ( [object]$InputObject, [string[]]$Key ) foreach ($k in $key) { $member = [enum]::GetNames('Pasm.Parameter.{0}' -f $k) foreach ($obj in $inputObject) { if ($obj.Contains($k)) { foreach ($o in $obj.$k) { if ($o -notin $member) { throw [FormatException]::new($('Invalid parameter value ''{0}'' detected. Allowed in ''{1}'': {2}.' -f $o, $k, $(Get-ArrayToString -Array $member))) } } } } } } # Yaml template validation: integer range function Test-PasmRange { param ( [object]$InputObject, [string[]]$Key, [int]$Start, [int]$End ) foreach ($k in $key) { foreach ($obj in $inputObject) { if ($obj.Contains($k)) { foreach ($o in $obj.$k) { if ($o -notin $start..$end) { throw [FormatException]::new($('The ''{0}'' is set to an invalid value of {1}, please set it in the range of {2} to {3}.' -f $k, $o, $start, $end)) } } } } } } # Yaml template validation: from-to function Test-PasmFromTo { param ( [object]$InputObject, [string]$From, [string]$To ) foreach ($obj in $inputObject) { if ($obj.Contains($from) -and $obj.Contains($to)) { if ($obj.$from -gt $obj.$to) { throw [FormatException]::new($('The value of FromPort({0}) exceeds ToPort({1}). Please set valid range.' -f $obj.$from, $obj.$to)) } } } } <# # Yaml template validation: boolean type function Test-PasmBoolean { param ( [object]$InputObject, [string[]]$Key ) foreach ($k in $key) { foreach ($obj in $inputObject) { if ($obj.Contains($k)) { foreach ($o in $obj.$k) { if ($o -notin 'true', 'false') { throw [FormatException]::new($('''{0}'' is boolean type. It must be set to either ''true'' or ''false''.' -f $k)) } } } } } } #> # Yaml template validation: 'ProfileName' function Test-PasmProfileName { param ( [object]$InputObject ) foreach ($obj in $inputObject) { if ($obj.Contains('ProfileName')) { foreach ($o in $obj.ProfileName) { if ($null -eq $(Get-AWSCredential -ProfileName $o)) { throw [FormatException]::new($('Invalid credential ''{0}'' detected. Please set valid profile name.' -f $o)) } } } } } # Yaml template validation: 'ServiceKey' function Test-PasmServiceKey { param ( [object]$InputObject ) foreach ($obj in $inputObject) { if ($obj.Contains('ServiceKey')) { foreach ($o in $obj.ServiceKey) { if ($o -notin (Get-AWSPublicIpAddressRange -OutputServiceKeys)) { throw [FormatException]::new($('Invalid service key ''{0}'' detected. Please set valid service key.' -f $o)) } } } } } # Yaml template validation: 'Region' function Test-PasmRegion { param ( [object]$InputObject ) foreach ($obj in $inputObject) { if ($obj.Contains('Region')) { foreach ($o in $obj.Region) { if ($o -notin (Get-AWSRegion -IncludeChina -IncludeGovCloud).Region) { throw [FormatException]::new($('Invalid region ''{0}'' detected. Please set valid region.' -f $o)) } } } } } # Yaml template validation: 'VpcId' function Test-PasmVpcId { param ( [string]$VpcId ) try { Get-EC2Vpc -VpcId $vpcId } catch { throw [ItemNotFoundException]::new($('The VPC with Id ''{0}'' not found.' -f $vpcId)) } } # Yaml template validation: 'SubnetId' function Test-PasmSubnetId { param ( [string[]]$SubnetId ) foreach ($id in $subnetId) { try { Get-EC2Subnet -SubnetId $id } catch { throw [ItemNotFoundException]::new($('The Subnet with Id ''{0}'' not found.' -f $id)) } } } # Parameter validation: 'MaxEntry' function Test-PasmMaxEntry { param ( [int]$Entry, [int]$MaxEntry, [Pasm.Parameter.IpFormat]$IpFormat, [Pasm.Parameter.Resource]$ResourceType, [Pasm.Parameter.EphemeralPort]$EphemeralPort = 'Default' ) if ($ResourceType -eq [Pasm.Parameter.Resource]::NetworkAcl) { $entry = $entry + 1 if ($ephemeralPort -eq 'Default') { $entry = $entry + 2 } } if ($entry -ge $maxEntry) { throw [InvalidOperationException]::new( $( 'The maximum number of {0} entries({1}) for the ''{2}'' you are trying to configure exceeds the quota limit({3}). Please review your settings. This entry number contains default entries.' -f $ipFormat, $entry, $resourceType, $maxEntry ) ) } } # Get Amazon.Util.AWSPublicIpAddressRange function Get-PasmAWSIpRange { param ( [object]$InputObject, [Pasm.Parameter.Resource]$Resource, [string]$AddressFamily ) $obj = if ($resource -eq [Pasm.Parameter.Resource]::SecurityGroup -or $resource -eq [Pasm.Parameter.Resource]::NetworkAcl) { if ($inputObject.Contains('Region') -and $inputObject.Contains('IpFormat')) { (Get-AWSPublicIpAddressRange -ServiceKey $inputObject.ServiceKey -Region $inputObject.Region).Where( { $_.IpAddressFormat -in $inputObject.IpFormat } ) } elseif (!$inputObject.Contains('Region') -and $inputObject.Contains('IpFormat')) { (Get-AWSPublicIpAddressRange -ServiceKey $inputObject.ServiceKey).Where( { $_.IpAddressFormat -in $inputObject.IpFormat } ) } elseif ($inputObject.Contains('Region') -and !$inputObject.Contains('IpFormat')) { (Get-AWSPublicIpAddressRange -ServiceKey $inputObject.ServiceKey -Region $inputObject.Region) } elseif (!$inputObject.Contains('Region') -and !$inputObject.Contains('IpFormat')) { (Get-AWSPublicIpAddressRange -ServiceKey $inputObject.ServiceKey) } else { return } } elseif ($resource -eq [Pasm.Parameter.Resource]::PrefixList) { if ($inputObject.Contains('Region')) { (Get-AWSPublicIpAddressRange -ServiceKey $inputObject.ServiceKey -Region $inputObject.Region).Where( { $_.IpAddressFormat -eq $addressFamily } ) } elseif (!$inputObject.Contains('Region')) { (Get-AWSPublicIpAddressRange -ServiceKey $inputObject.ServiceKey).Where( { $_.IpAddressFormat -eq $addressFamily } ) } else { return } } return $obj } # Create resource entry object: 'SecurityGroup' function New-PasmSecurityGroupEntry { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([void])] param ( [object[]]$Rule ) if ($PSCmdlet.ShouldProcess('SecurityGroup.Rules')) { $ipPermissions = [list[IpPermission]]::new() foreach ($r in $rule) { $ipPermission = [IpPermission]::new() $ipPermission.IpProtocol = $r.Protocol $ipPermission.FromPort = $r.FromPort $ipPermission.ToPort = $r.ToPort if ($r.Protocol -in 'icmp', 'icmpv6') { $ipPermission.FromPort = '-1' $ipPermission.ToPort = '-1' } foreach ($range in $r.Ranges) { if ($range.IpFormat -eq 'IPv4') { $ipv4Range = [IpRange]::new() $ipv4Range.CidrIp = $range.IpPrefix $ipv4Range.Description = $range.Description $ipPermission.Ipv4Ranges.Add($ipv4Range) } if ($range.IpFormat -eq 'IPv6') { $ipv6Range = [Ipv6Range]::new() $ipv6Range.CidrIpv6 = $range.IpPrefix $ipv6Range.Description = $range.Description $ipPermission.Ipv6Ranges.Add($ipv6Range) } } $ipPermissions.Add($ipPermission) } return $ipPermissions } } # Create resource entry object: 'NetworkAcl' function New-PasmNetworkAclEntry { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([void])] param ( [object]$InputObject, [NetworkAcl]$NetworkAcl ) if ($PSCmdlet.ShouldProcess('NetworkAcl.Rules')) { if ($inputObject.Contains('Rules')) { foreach ($r in $inputObject.Rules) { foreach ($range in $r.Ranges) { $param = @{ NetworkAclId = $networkAcl.NetworkAclId Protocol = [Pasm.Parameter.Protocol]::$($r.Protocol).value__ PortRange_From = $r.FromPort PortRange_To = $r.ToPort RuleAction = 'allow' RuleNumber = $range.RuleNumber } if ($range.IpFormat -eq 'Ipv4') { $param.Add('CidrBlock', $range.IpPrefix) } if ($range.IpFormat -eq 'Ipv6') { $param.Add('Ipv6CidrBlock', $range.IpPrefix) } if ($r.Protocol -in 'icmp', 'icmpv6') { $param.Add('IcmpTypeCode_Code', '-1') $param.Add('IcmpTypeCode_Type', '-1') } if ($inputObject.Contains('FlowDirection')) { if ($inputObject.FlowDirection -eq 'Ingress') { $param.Add('Egress', $false) } if ($inputObject.FlowDirection -eq 'Egress') { $param.Add('Egress', $true) } } New-EC2NetworkAclEntry @param } } } else { return } if (!($inputObject.Contains('EphemeralPort')) -or (($inputObject.Contains('EphemeralPort')) -and ($inputObject.EphemeralPort -eq 'Default'))) { $paramIpv4 = @{ NetworkAclId = $networkAcl.NetworkAclId Protocol = 6 PortRange_From = 1024 PortRange_To = 65535 RuleAction = 'allow' RuleNumber = 32765 CidrBlock = '0.0.0.0/0' Egress = $null } $paramIpv6 = @{ NetworkAclId = $networkAcl.NetworkAclId Protocol = 6 PortRange_From = 1024 PortRange_To = 65535 RuleAction = 'allow' RuleNumber = 32766 Ipv6CidrBlock = '::/0' Egress = $null } $paramIpv4.Egress = $false New-EC2NetworkAclEntry @paramIpv4 $paramIpv6.Egress = $false New-EC2NetworkAclEntry @paramIpv6 $paramIpv4.Protocol = -1 $paramIpv4.PortRange_From = $null $paramIpv4.PortRange_To = $null $paramIpv4.Egress = $true New-EC2NetworkAclEntry @paramIpv4 $paramIpv6.Protocol = -1 $paramIpv6.PortRange_From = $null $paramIpv6.PortRange_To = $null $paramIpv6.Egress = $true New-EC2NetworkAclEntry @paramIpv6 } if ($inputObject.Contains('AssociationSubnetId')) { $filter = @{ Name = 'association.subnet-id' Values = @($inputObject.AssociationSubnetId) } $associationIds = (Get-EC2NetworkAcl -Filter $filter).Associations.Where( { $_.SubnetId -in $filter.Values } ).NetworkAclAssociationId foreach ($associationId in $associationIds) { Set-EC2NetworkAclAssociation -NetworkAclId $networkAcl.NetworkAclId -AssociationId $associationId | Out-Null } } } } # Create resource entry object: 'PrefixList' function New-PasmPrefixListEntry { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([void])] param ( [object[]]$Rule ) if ($PSCmdlet.ShouldProcess('PrefixList.Rules')) { $entries = [list[AddPrefixListEntry]]::new() foreach ($r in $rule) { foreach ($range in $r.Ranges) { $entry = [AddPrefixListEntry]::new() $entry.Cidr = $range.IpPrefix $entry.Description = $range.Description $entries.Add($entry) } } return $entries } } |