/
dump.go
103 lines (84 loc) · 2.15 KB
/
dump.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package dumpcft
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
cfntypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
type Dumper struct {
CloudFormationClient *cloudformation.Client
STSClient *sts.Client
OutputDir string
}
func (d Dumper) Dump(ctx context.Context) (int, error) {
var stacks []cfntypes.Stack
var nextToken *string
for {
input := cloudformation.DescribeStacksInput{
NextToken: nextToken,
}
resp, err := d.CloudFormationClient.DescribeStacks(ctx, &input)
if err != nil {
return 0, err
}
for _, s := range resp.Stacks {
stacks = append(stacks, s)
}
if nextToken == nil {
break
}
}
for _, stack := range stacks {
input := cloudformation.GetTemplateInput{
StackName: stack.StackName,
}
resp, err := d.CloudFormationClient.GetTemplate(ctx, &input)
if err != nil {
return 0, err
}
arn, err := arn.Parse(*stack.StackId)
if err != nil {
return 0, err
}
accountId, err := d.awsAccountId(ctx)
if err != nil {
return 0, err
}
filename := d.buildFilename(accountId, arn.Region, *stack.StackName, "yaml")
if isJSON([]byte(*resp.TemplateBody)) {
filename = d.buildFilename(accountId, arn.Region, *stack.StackName, "json")
}
file, err := os.Create(filename) // #nosec G304 -- AWS is in trust boundary
if err != nil {
return 0, err
}
defer func() {
if err := file.Close(); err != nil {
// TODO print error
}
}()
_, err = file.WriteString(*resp.TemplateBody)
if err != nil {
return 0, err
}
}
return len(stacks), nil
}
func (d Dumper) buildFilename(accountId, region, stackName, ext string) string {
return fmt.Sprintf("%s/%s.%s.%s.cfn.%s", d.OutputDir, accountId, region, stackName, ext)
}
func (d Dumper) awsAccountId(ctx context.Context) (string, error) {
resp, err := d.STSClient.GetCallerIdentity(ctx, nil)
if err != nil {
return "", err
}
return *resp.Account, nil
}
func isJSON(content []byte) bool {
var js json.RawMessage
return json.Unmarshal(content, &js) == nil
}