Skip to content

Instantly share code, notes, and snippets.

@niski84
Last active April 27, 2024 22:04
Show Gist options
  • Save niski84/d4fd4dc2d0d320e464a97d57d5fc7501 to your computer and use it in GitHub Desktop.
Save niski84/d4fd4dc2d0d320e464a97d57d5fc7501 to your computer and use it in GitHub Desktop.
find orphaned databases
package main
import (
"context"
"encoding/json"
"fmt"
"net/url"
"os"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
)
type (
DBConnectionDetails struct {
Hostname string
DBName string
}
DatabaseInfo struct {
Hostname string
Databases map[string]bool
}
RDSDatabase struct {
Hostname string
Databases []string
}
)
func ParseDBConnectionString(connStr string) (DBConnectionDetails, error) {
dbURL, err := url.Parse(connStr)
if err != nil {
return DBConnectionDetails{}, fmt.Errorf("failed to parse connection string: %s, error: %v", connStr, err)
}
details := DBConnectionDetails{
Hostname: dbURL.Hostname(),
}
pathSegments := strings.Split(dbURL.Path, "/")
if len(pathSegments) > 1 {
details.DBName = pathSegments[1]
}
return details, nil
}
func GetActiveSecretsDatabases() ([]DatabaseInfo, error) {
cfg, err := config.LoadDefaultConfig(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to load AWS config: %v", err)
}
svc := secretsmanager.NewFromConfig(cfg)
dbMap := make(map[string]*DatabaseInfo)
paginator := secretsmanager.NewListSecretsPaginator(svc, &secretsmanager.ListSecretsInput{})
for paginator.HasMorePages() {
page, err := paginator.NextPage(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to paginate secrets: %v", err)
}
for _, secret := range page.SecretList {
// Skip secrets based on path suffix
if strings.HasSuffix(*secret.Name, "/cl") || strings.HasSuffix(*secret.Name, "/cp") || strings.HasSuffix(*secret.Name, "/Palos") {
fmt.Printf("Skipping secret %s\n", *secret.Name)
continue
}
time.Sleep(3 * time.Second) // 3-second pause for tracing
fmt.Printf("Processing secret: %s\n", *secret.Name)
result, err := svc.GetSecretValue(context.Background(), &secretsmanager.GetSecretValueInput{
SecretId: aws.String(*secret.Name),
})
if err != nil {
fmt.Printf("Error retrieving secret %s: %v\n", *secret.Name, err)
continue
}
var secretMap map[string]interface{}
if err := json.Unmarshal([]byte(*result.SecretString), &secretMap); err != nil {
fmt.Printf("Error unmarshaling secret %s: %v\n", *secret.Name, err)
continue
}
for key, value := range secretMap {
if strings.HasPrefix(key, "dbconnectionstring") && value != nil {
connStr, ok := value.(string)
if !ok {
fmt.Printf("Non-string DB connection string for key %s in secret %s\n", key, *secret.Name)
continue
}
dbDetails, err := ParseDBConnectionString(connStr)
if err != nil {
fmt.Printf("Error parsing DB connection string for key %s in secret %s: %v\n", key, *secret.Name, err)
continue
}
fmt.Printf("Parsed Hostname: %s, DBName: %s\n", dbDetails.Hostname, dbDetails.DBName)
if dbInfo, exists := dbMap[dbDetails.Hostname]; !exists {
dbMap[dbDetails.Hostname] = &DatabaseInfo{
Hostname: dbDetails.Hostname,
Databases: map[string]bool{dbDetails.DBName: true},
}
} else {
dbInfo.Databases[dbDetails.DBName] = true
}
}
}
}
}
var databases []DatabaseInfo
for _, dbInfo := range dbMap {
databases = append(databases, *dbInfo)
}
return databases, nil
}
func main() {
databases, err := GetActiveSecretsDatabases()
if err != nil {
fmt.Println("Error getting active databases:", err)
os.Exit(1)
}
fmt.Println("Active databases collected successfully:")
for _, dbInfo := range databases {
fmt.Printf("Hostname: %s, Databases: %v\n", dbInfo.Hostname, dbInfo.Databases)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment