Skip to content

Instantly share code, notes, and snippets.

@panasenco
Last active April 30, 2025 16:07
Show Gist options
  • Save panasenco/47a4f097bbfe263ad09a35f5defbbc64 to your computer and use it in GitHub Desktop.
Save panasenco/47a4f097bbfe263ad09a35f5defbbc64 to your computer and use it in GitHub Desktop.
Bulk tagging AWS resources from a spreadsheet - see blog post: https://dev.to/panasenco/tag-aws-from-spreadsheet-3ild. Install from https://www.powershellgallery.com/packages/Export-AwsTags with `Install-Module Export-AwsTags -Scope CurrentUser`
#
# Module manifest for module 'Export-AwsTags'
#
@{
ModuleVersion = '1.1'
GUID = '56367acd-8b8b-4c62-bc9e-f43aab12ac3c'
Author = 'Aram Panasenco'
Copyright = '(c) 2025 Aram Panasenco. All rights reserved.'
Description = 'Bulk export tags from a CSV file into AWS'
PowerShellVersion = '5.1'
NestedModules = 'Export-AwsTags.psm1'
FunctionsToExport = @('Export-AwsTags', 'Import-AutoScalingGroupTags', 'Update-CsvColumnOrder')
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
FileList = @('Export-AwsTags.psd1','Export-AwsTags.psm1')
PrivateData = @{
PSData = @{
Tags = @('AWS')
LicenseUri = 'https://mit-license.org/'
ProjectUri = 'https://gist.github.com/panasenco/47a4f097bbfe263ad09a35f5defbbc64'
}
}
HelpInfoURI = 'https://dev.to/panasenco/tag-aws-from-spreadsheet-3ild'
}
<#
.Synopsis
Bulk export tags from a CSV file into AWS
.Description
AWS Tag Editor allows a bulk download of tag values to a CSV file, but doesn't allow a CSV file with modifications to be uploaded back.
This PowerShell function bridges that gap.
Simply download the CSV file from the bulk editor, change the tags, then pass the path to this function along with the list of tags you changed.
Note that this function can not remove tags, only add or change their values.
.Parameter CsvPath
Path to the CSV spreadsheet containing the column ARN along with "Tag: <Tag name>" columns.
NOTE that values of '(not tagged)' are not sent to AWS at all, so changing a tag value to '(not tagged)' will not actually remove that tag.
Also note that values of '-' are converted to empty strings.
.Parameter ExportTags
List of tags to export to AWS from the spreadsheet.
All other columns are ignored.
.Parameter AwsProfile
Name of the AWS profile to use.
.Example
Export-AwsTags -CsvPath ~\Downloads\resources.csv -ExportTags @('My important tag 1', 'My important tag 2') -AwsProfile dev
#>
function Export-AwsTags {
param (
[Parameter(Mandatory=$true)][string] $CsvPath,
[Parameter(Mandatory=$true)][string[]] $ExportTags,
[Parameter()][string] $AwsProfile = 'default'
)
Import-Csv -Path $CsvPath | foreach {
$Row = $_
$Payload = [pscustomobject]@{
ResourceARNList = @($Row.ARN)
Tags = [pscustomobject]@{}
}
$ExportTags | foreach {
$Value = $Row."Tag: $_"
if ($Value -ne '(not tagged)') {
if ($Value -eq '-') {
$Value = ''
}
Add-Member -InputObject $Payload.Tags -MemberType NoteProperty -Name $_ -Value $Value
}
}
$PayloadFile = New-TemporaryFile
New-Item -Force -Path $PayloadFile -Value ($Payload | ConvertTo-Json -Compress) | Out-Null
Write-Host "Tagging AWS resource $($Row.ARN)"
aws resourcegroupstaggingapi tag-resources --profile $AwsProfile --cli-input-json "file://$PayloadFile"
}
}
<#
.Synopsis
Import tags for autoscaling groups in a format that's compatible with the output of AWS Tag Editor.
.Description
The AWS Tag Editor and the Resource Groups Tagging API don't currently support fetching tags for EC2 autoscaling groups.
This PowerShell function bridges that gap.
.Parameter CsvPath
Path to the CSV spreadsheet to export to.
.Parameter AwsProfile
Name of the AWS profile to use
.Example
Import-AutoScalingGroupTags -CsvPath ~\Downloads\dev-asg-tags.csv -AwsProfile dev
#>
function Import-AutoScalingGroupTags {
param (
[Parameter(Mandatory=$true)][string] $CsvPath,
[Parameter()][string] $AwsProfile = 'default'
)
$AutoScalingGroups = (aws autoscaling describe-auto-scaling-groups --profile $AwsProfile | ConvertFrom-Json).AutoScalingGroups
$Columns = $AutoScalingGroups.Tags.Key | sort | Get-Unique
$AutoScalingGroups | foreach {
$Row = [pscustomobject]@{ARN = $_.AutoScalingGroupARN}
$Tags = $_.Tags
$Columns | foreach {
$Column = $_
$MatchingTags = $Tags | where { $_.Key -eq $Column }
if (($MatchingTags | Measure-Object).Count -gt 0) {
$Value = $MatchingTags[0].Value
} else {
$Value = '(not tagged)'
}
$Row | Add-Member -MemberType NoteProperty -Name "Tag: $Column" -Value $Value
}
$Row
} | Export-Csv -Path $CsvPath -NoTypeInformation
}
<#
.Synopsis
Reorder columns in a CSV file to make certain columns appear first.
.Description
Given a list of "first columns", makes those columns appear first in a given CSV file in that exact order.
All other columns will appear after in their original order.
Helpful in manually editing massive autogenerated CSV files.
.Parameter CsvPath
Path to the CSV spreadsheet to be reordered.
.Parameter FirstColumns
List of columns that must appear first in the CSV file.
If a column doesn't exist, it will be inserted with blank values.
.Parameter DefaultValue
Default value to use when a column from FirstColumns doesn't exist.
.Example
Update-CsvColumnOrder -CsvPath ~\Downloads\resources.csv -FirstColumns @('ARN', 'Tag: My important tag 1', 'Tag: My important tag 2') -DefaultValue '(not tagged)'
#>
function Update-CsvColumnOrder {
param (
[Parameter(Mandatory=$true)][string] $CsvPath,
[Parameter(Mandatory=$true)][string[]] $FirstColumns,
[Parameter()][string] $DefaultValue = ''
)
$SortedRows = Import-Csv -Path $CsvPath | foreach {
$Row = $_
$Columns = $Row | Get-Member -Type NoteProperty
$RemainingColumns = for ($i = 0; $i -lt $FirstColumns.count; $i++) {
[pscustomobject]@{
Name = $FirstColumns[$i]
SortIndex = $i
New = $true
}
}
for ($i = 0; $i -lt $Columns.count; $i++) {
$FirstColumnIndex = $FirstColumns.IndexOf($Columns[$i].Name)
if ($FirstColumnIndex -ge 0) {
$SortIndex = $FirstColumnIndex
$RemainingColumns = $RemainingColumns | where { $_.Name -ne $Columns[$i].Name}
} else {
$SortIndex = $FirstColumns.count + $i
}
$Columns[$i] | Add-Member -MemberType NoteProperty -Name SortIndex -Value $SortIndex
}
$SortedRow = [pscustomobject]@{}
($Columns + $RemainingColumns) | sort { $_.SortIndex } | foreach {
if ($_.New) {
$Value = $DefaultValue
} else {
$Value = $Row.($_.Name)
}
Add-Member -InputObject $SortedRow -Type NoteProperty -Name $_.Name -Value $Value
}
$SortedRow
}
if (($SortedRows | Measure-Object).Count -gt 0) {
$SortedRows | Export-Csv -Path $CsvPath -NoTypeInformation
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment