AWSPriceList.psm1
$script:PriceListBaseUrl = "https://pricing.us-east-1.amazonaws.com" Function Get-AWSPriceListOffersIndexFile { <# .SYNOPSIS Retrieves the contents of the offers index file. .DESCRIPTION The cmdlet retrieves the base offer index file from https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json. It can be returned as a PSCustomObject or a JSON string. .PARAMETER AsJson Specifies that the content is returned as a JSON string instead of a PSCustomObject. .EXAMPLE Get-AWSPriceListOffersIndexFile Retrieves all of the available services. .INPUTS None .OUTPUTS System.Management.Automation.PSCustomObject, System.String .NOTES AUTHOR: Michael Haken LAST UPDATE: 1/16/2019 #> [CmdletBinding()] Param( [Parameter()] [Switch]$AsJson ) Begin { } Process { [System.String]$OfferIndexUrl = "$script:PriceListBaseUrl/offers/v1.0/aws/index.json" [Microsoft.PowerShell.Commands.WebResponseObject]$Response = Invoke-WebRequest -Uri $OfferIndexUrl -Method Get if ($AsJson) { Write-Output -InputObject ([System.Text.Encoding]::UTF8).GetString($Response.Content) } else { Write-Output -InputObject (ConvertFrom-Json -InputObject ([System.Text.Encoding]::UTF8).GetString($Response.Content)) } } End { } } Function Get-AWSPriceListCurrentOfferUrls { <# .SYNOPSIS Retrieves a list of the offer index files for each available service. .DESCRIPTION The cmdlet retrieves the base offer index file from https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json. It parses out the urls for the available services. These urls can be used to retrieve the specific pricing information for those services. .EXAMPLE Get-AWSPriceListCurrentOfferUrl Retrieves all of the offer index file urls. .INPUTS None .OUTPUTS System.String[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 1/16/2019 #> [CmdletBinding()] Param( ) Begin { } Process { [PSCustomObject]$IndexFileContents = Get-AWSPriceListOffersIndexFile [System.String[]]$Results = @() $IndexFileContents.offers | Get-Member -MemberType *Property | ForEach-Object { try { $Name = $_.Name $Results += "$script:PriceListBaseUrl$($IndexFileContents.offers | Select-Object -ExpandProperty $_.Name | Select-Object -ExpandProperty currentVersionUrl)" } catch { Write-Warning -Message "Error parsing $Name : $_.Exception.Message" } } Write-Output -InputObject $Results } End { } } Function Get-AWSPriceListServices { <# .SYNOPSIS Retrieves a list of the available services in the price list api. .DESCRIPTION The cmdlet retrieves the base offer index file from https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json. It parses out the the available services from the offer file. .EXAMPLE Get-AWSPriceListServices Retrieves all of the available services. .INPUTS None .OUTPUTS System.String[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 1/16/2019 #> [CmdletBinding()] Param( ) Begin { } Process { [PSCustomObject]$IndexFileContents = Get-AWSPriceListOffersIndexFile [System.String[]]$Results = @() $IndexFileContents.offers | Get-Member -MemberType *Property | ForEach-Object { try { $Name = $_.Name $Results += $Name } catch { Write-Warning -Message "Error parsing $Name : $_.Exception.Message" } } Write-Output -InputObject $Results } End { } } Function Get-AWSPriceListProductInformation { <# .SYNOPSIS This cmdlet evaluates the data in the AWS Price List API json and returns information about products that match the search criteria. .DESCRIPTION The cmdlet parses the json in a specified file on disk retrieved from the price list API or downloads it directly from the provided Url. It matches products against the specified attributes. This is useful to find say all of the different SKUs and Operation codes for db.m4.large instances in US East (N. Virginia). .PARAMETER Path The path to the downloaded price list API file. .PARAMETER Url The Url containing the price list information for the product you want. .PARAMETER Product The product you want to download price list information for. .PARAMETER Filter The attributes used to match specific skus in the price list API. The filter will look like: @{"location" = "US East (N. Virginia)"; "instanceType" = "db.m4.large"; "databaseEngine" = "PostgreSQL"}. The key values will be matched against the product attribute key values. .EXAMPLE Get-AWSProductInformation -Product AmazonRDS -Filter @{"location" = "US East (N. Virginia)"; "instanceType" = "db.m4.large"; "databaseEngine" = "PostgreSQL"} Gets matching RDS skus for the attributes specified .EXAMPLE Get-AWSPriceListProductInformation -Url https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonRDS/current/index.json -Filter @{"location" = "US East (N. Virginia)"; "instanceType" = "db.m4.large"; "databaseEngine" = "PostgreSQL"} Gets matching RDS skus for the attributes specified .EXAMPLE Get-AWSPriceListProductInformation -Product AmazonEC2 -Filter @{"location" = "US East (N. Virginia)"; "instanceType" = "m4.large"} Gets matching EC2 skus for the attributes specified .EXAMPLE Get-AWSPriceListProductInformation -Path index.json -Filter @{"location" = "US East (N. Virginia)"; "instanceType" = "m4.large"} Gets matching EC2 skus for the attributes specified .INPUTS None .OUTPUTS System.Management.Automation.PSCustomObject .NOTES AUTHOR: Michael Haken LAST UPDATE: 1/16/2019 #> [CmdletBinding(DefaultParameterSetName = "Path")] Param( [Parameter(Mandatory = $true, ParameterSetName = "Path")] [ValidateScript({Test-Path $_})] [System.String]$Path, [Parameter(Mandatory = $true)] [ValidateNotNull()] [System.Collections.Hashtable]$Filter ) DynamicParam { [System.Management.Automation.RuntimeDefinedParameterDictionary]$ParamDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary [PSCustomObject]$OfferIndexFile = Get-AWSPriceListOffersIndexFile [System.String[]]$Urls = @() [System.String[]]$Products = @() $OfferIndexFile.offers | Get-Member -MemberType *Property | ForEach-Object { try { $Products += $_.Name $Urls += "$script:PriceListBaseUrl$($OfferIndexFile.offers | Select-Object -ExpandProperty $_.Name | Select-Object -ExpandProperty currentVersionUrl)" } catch { Write-Verbose -Message "Error parsing $Name : $_.Exception.Message" } } New-DynamicParameter -Name "Url" -Type ([System.Uri]) -ValidateSet $Urls -Mandatory -ParameterSets "Url" -ValidateNotNullOrEmpty -RuntimeParameterDictionary $ParamDictionary | Out-Null New-DynamicParameter -Name "Product" -Type ([System.String]) -ValidateSet $Products -Mandatory -ParameterSets "Product" -ValidateNotNullOrEmpty -RuntimeParameterDictionary $ParamDictionary | Out-Null Write-Output -InputObject $ParamDictionary } Begin { } Process { switch ($PSCmdlet.ParameterSetName) { "Url" { [System.String]$private:Response = [System.Text.Encoding]::UTF8.GetString((Invoke-WebRequest -Uri $PSBoundParameters["Url"] -Method Get | Select-Object -ExpandProperty Content)) break } "Product" { $private:Url = "$script:PriceListBaseUrl$($OfferIndexFile.offers | Select-Object -ExpandProperty $PSBoundParameters["Product"] | Select-Object -ExpandProperty currentVersionUrl)" [System.String]$private:Response = [System.Text.Encoding]::UTF8.GetString((Invoke-WebRequest -Uri $private:Url -Method Get | Select-Object -ExpandProperty Content)) break } "Path" { [System.String]$private:Response = Get-Content -Path $Path -Raw break } default { throw "The parameter set $($PSCmdlet.ParameterSetName) was not recognized." } } <# The converted Obj object will look like the following: formatVersion : v1.0 disclaimer : This pricing list is for informational purposes only. All prices are subject to the additional terms included in the pricing pages on http://aws.amazon.com. All Free Tier prices are also subject to the terms included at https://aws.amazon.com/free/ offerCode : AmazonElastiCache version : 20170419194925 publicationDate : 2017-04-19T19:49:25Z products : @{HBRQZSXXSY2DXJ77=; 3Y8QARGM5NXC9EBW=; ... } terms : @{OnDemand=; Reserved=} #> $private:ConvertedResponse = ConvertFrom-Json -InputObject $private:Response [PSCustomObject[]]$private:Results = @() # Expanding the products property gets us a single object with members like # RBW79EQZWRSDB85D : @{sku=RBW79EQZWRSDB85D; productFamily=Database Instance; attributes=} # W3PUKFKG7RDK3KA5 : @{sku=W3PUKFKG7RDK3KA5; productFamily=Data Transfer; attributes=} # We want to expand the property of the products object for each sku to access the hash table that has the data <# Products will look like 8W42JWEZE64YAUET : @{sku=8W42JWEZE64YAUET; productFamily=Cache Instance; attributes=} T64VHYZ5FZP9JDEC : @{sku=T64VHYZ5FZP9JDEC; productFamily=Cache Instance; attributes=} #> [PSCustomObject]$private:Products = $private:ConvertedResponse | Select-Object -ExpandProperty products # Getting the members of Products will get us all of the sku properties, we want to iterate each # one and select it, expanded from the products object, which will provide the hash table of data # which includes sku, productFamily, and attributes Get-Member -InputObject $private:Products -MemberType *Property | ForEach-Object { # The Get-Member results will have a name property, that is the sku data for each product # By expanding the name property, we get the values of the sku index, which are the properties # like attributes and productfamily [PSCustomObject]$private:ProductData = $private:Products | Select-Object -ExpandProperty $_.Name [System.Collections.Hashtable]$private:TempHashTable = @{} # Convert the PSCustomObject to a hash table $private:ProductData.attributes.psobject.Properties | ForEach-Object { $private:TempHashTable[$_.Name] = $_.Value } # Assume the product matches the filters, and prove it false $private:Matches = $true # Now that we have product object, we can filter based on the key value pairs provided foreach ($Key in $Filter.Keys) { # If the hash table doesn't contain the key and the values are not alike, it doesn't match # Otherwise, keep going if (-not ($private:TempHashTable.ContainsKey($Key) -and $private:TempHashTable[$Key] -like $Filter[$Key])) { $private:Matches = $false break } } if ($private:Matches -eq $true) { $private:Results += [PSCustomObject]@{"Sku" = $private:ProductData.sku; "ProductFamily" = $private:ProductData.productFamily; "Attributes" = $TempHashTable} } } Write-Output -InputObject $private:Results } End { } } |