casts
Cross account STS for AWS API execution using golang.
The CASTS package provides a simple way to work with the Amazon AWS API across multiple accounts using STS authentication. It starts with simple functions to work with your shard credentials to begin the process of STS access. It also provides a structure to manage a list of accounts with which to iteract with.
Example of accounts.json file
This is an example of an account.json file and the fields it requires, This file is used to know how to connect to AWS accounts using AWS STS access.
{
"accounts": [
{
"account_number": "<aws_account_number>",
"account_name": "<aws_profile_name>",,
"supported_regions": ["<aws_region_1>","<aws_region_2>","<aws_region_3>"],
"profile_name": "<aws_profile_name>",
"profile_region": "<aws_region>",
"sts_external_id": "<aws_external_id>",
"sts_role_arn": "arn:aws:iam::%s:role/<sts_role_to_assume>"
}
]
}
Fixed list of accounts example
This example shows how to execute a function on a single AWS account.
package main
import (
"context"
"flag"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/dsledge/casts"
)
type TestOutput struct {
Name string
}
func main() {
file_accounts := flag.String("accounts", "accounts.json", "JSON file listing accounts for STS Access")
flag.Parse()
// Load SDK configuration with shared config profile.
err := casts.Configure(*file_accounts)
if err != nil {
log.Fatal(err)
}
// Function to run through the account iterator
testFunc := func(input *casts.Input, out casts.Output) {
// Iterate over the region list
for _, region := range input.Account.SupportedRegions {
input.Config.Region = region
svc := s3.NewFromConfig(*input.Config)
// Call the list buckets api operation
listBucketsResult, err := svc.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
if err != nil {
panic("Couldn't list buckets")
}
// Iterate over the results and print out the buckets
for _, bucket := range listBucketsResult.Buckets {
fmt.Printf("Account: %s \tRegion: %s \tBucket: %s\n", input.Account.AccountName, region, *bucket.Name)
}
// Assert the object type from the interface to the known object type
testoutput := out.(*TestOutput)
testoutput.Name = "Test"
}
}
output := TestOutput{}
err = casts.ExecuteOnAccounts(&[]string{"123456789"}, testFunc, &output)
if err != nil {
fmt.Printf("Error iterating accounts: %s\n", err)
}
fmt.Printf("TESTING OUTPUT: %s\n", output.Name)
}
Run against all accounts example
This example shows how to execute a function across multiple accounts.
package main
import (
"context"
"flag"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/dsledge/casts"
)
type TestOutput struct {
Name string
}
func main() {
file_accounts := flag.String("accounts", "accounts.json", "JSON file list of accounts for STS Access")
flag.Parse()
// Load SDK configuration with shared config profile.
err := casts.Configure(*file_accounts)
if err != nil {
log.Fatal(err)
}
// Function to run through the account iterator
testFunc := func(input *casts.Input, out casts.Output) {
// Iterate over the region list
for _, region := range input.Account.SupportedRegions {
input.Config.Region = region
svc := s3.NewFromConfig(*input.Config)
// Call the list buckets api operation
listBucketsResult, err := svc.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
if err != nil {
panic("Couldn't list buckets")
}
// Iterate over the results and print out the buckets
for _, bucket := range listBucketsResult.Buckets {
fmt.Printf("Account: %s \tRegion: %s \tBucket: %s\n", input.Account.AccountName, region, *bucket.Name)
}
// Assert the object type from the interface to the known object type
testoutput := out.(*TestOutput)
testoutput.Name = "Test"
}
}
// Execute function across all accounts found in the accounts.json file.
output := TestOutput{}
err = casts.ExecuteOnAccounts(nil, testFunc, &output)
if err != nil {
fmt.Printf("Error iterating accounts: %s\n", err)
}
// Print the information appended to the output struct.
fmt.Printf("TESTING OUTPUT: %s\n", output.Name)
}