-
Notifications
You must be signed in to change notification settings - Fork 1
/
iamy.go
142 lines (123 loc) · 4.72 KB
/
iamy.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"gopkg.in/alecthomas/kingpin.v2"
)
const versionTooOldError = "Your version of IAMy (%s) is out of date compared to what the local project expects. You should upgrade to %s to use this project.\n"
const buildVersionMismatch = "Your version of IAMy (%s) does not match the build tag (%s) the local project requires. You should upgrade to %s to use this project.\n"
var (
Version string = "dev"
defaultDir string
dryRun *bool
versionFileName string = ".iamy-version"
configFileName string = ".iamy-flags"
)
type logWriter struct{ *log.Logger }
func (w logWriter) Write(b []byte) (int, error) {
w.Printf("%s", b)
return len(b), nil
}
type Ui struct {
*log.Logger
Error, Debug *log.Logger
Exit func(code int)
}
// CFN automatically tags resources with this and other tags:
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html
const cloudformationStackNameTag = "aws:cloudformation:stack-name"
func main() {
var (
debug = kingpin.Flag("debug", "Show debugging output").Bool()
skipCfnTagged = kingpin.Flag("skip-cfn-tagged", fmt.Sprintf("Shorthand for --skip-tagged %s", cloudformationStackNameTag)).Bool()
skipTagged = kingpin.Flag("skip-tagged", "Skips IAM entities (or buckets associated with bucket policies) tagged with a given tag").Strings()
includeTagged = kingpin.Flag("include-tagged", "Includes IAM entities (or buckets associated with bucket policies) tagged with a given tag").Strings()
skipPathPrefixes = kingpin.Flag("skip-path-prefix", fmt.Sprintf("Skips IAM entities that have a path starting with the supplied prefix, repeat flag for multiple prefixes")).Strings()
pull = kingpin.Command("pull", "Syncs IAM users, groups and policies from the active AWS account to files")
pullDir = pull.Flag("dir", "The directory to dump yaml files to").Default(defaultDir).Short('d').String()
pullCanDelete = pull.Flag("delete", "Delete extraneous files from destination dir").Bool()
lookupCfn = pull.Flag("accurate-cfn", "Fetch all known resource names from cloudformation to get exact filtering").Bool()
push = kingpin.Command("push", "Syncs IAM users, groups and policies from files to the active AWS account")
pushDir = push.Flag("dir", "The directory to load yaml files from").Default(defaultDir).Short('d').ExistingDir()
format = kingpin.Command("fmt", "Update YAML files to match expected format")
formatDir = format.Flag("dir", "The base directory to format").Default(defaultDir).Short('d').ExistingDir()
formatCanDelete = format.Flag("delete", "Delete extraneous files from destination dir").Bool()
)
dryRun = kingpin.Flag("dry-run", "Show what would happen, but don't prompt to do it").Bool()
kingpin.Version(Version)
kingpin.CommandLine.Help =
`Read and write AWS IAM users, policies, groups and roles from YAML files.`
ui := Ui{
Logger: log.New(os.Stdout, "", 0),
Error: log.New(os.Stderr, "", 0),
Debug: log.New(ioutil.Discard, "", 0),
Exit: os.Exit,
}
args := os.Args[1:]
var configFileArgs []string
if _, err := os.Stat(configFileName); err == nil {
configFileArgs, err = kingpin.ExpandArgsFromFile(configFileName)
if err != nil {
panic(err)
}
args = append(args, configFileArgs...)
}
cmd, err := kingpin.CommandLine.Parse(args)
if err != nil {
panic(err)
}
if *debug {
ui.Debug = log.New(os.Stderr, "DEBUG ", log.LstdFlags)
log.SetFlags(0)
log.SetOutput(&logWriter{ui.Debug})
if len(configFileArgs) > 0 {
ui.Debug.Printf("Found flags in %s: %s", configFileName, configFileArgs)
}
} else {
log.SetOutput(ioutil.Discard)
}
if err := checkVersion(); err != nil {
panic(err)
}
if *skipCfnTagged {
*skipTagged = append(*skipTagged, cloudformationStackNameTag)
}
switch cmd {
case push.FullCommand():
PushCommand(ui, PushCommandInput{
Dir: *pushDir,
HeuristicCfnMatching: !*lookupCfn,
SkipTagged: *skipTagged,
IncludeTagged: *includeTagged,
SkipPathPrefixes: *skipPathPrefixes,
})
case pull.FullCommand():
PullCommand(ui, PullCommandInput{
Dir: *pullDir,
CanDelete: *pullCanDelete,
HeuristicCfnMatching: !*lookupCfn,
SkipTagged: *skipTagged,
IncludeTagged: *includeTagged,
SkipPathPrefixes: *skipPathPrefixes,
})
case format.FullCommand():
FormatCommand(ui, FormatCommandInput{
Dir: *formatDir,
CanDelete: *formatCanDelete,
})
}
}
func init() {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
dir, err = filepath.EvalSymlinks(dir)
if err != nil {
panic(err)
}
defaultDir = filepath.Clean(dir)
}