Parse a text file and split sections based on vertical anchors
// vertSplitter parses an AWS credentials file and splits the sections | |
// up based on profiles. It then parses each section to build a | |
// JSON profile struct for each profile including comments. | |
package main | |
import ( | |
"bufio" | |
"encoding/json" | |
"fmt" | |
"os" | |
"regexp" | |
) | |
// readFileAnchorChunks takes in input filename and regex string | |
// it then reads the file and breaks the contents up in to a map of | |
// indexed string slices based on the anchor provided. It's essentially | |
// vertically splitting a file based on a regex separator. | |
func readFileAnchorChunks(filename string, anchorRegex string) (chunks map[int][]string, err error) { | |
chunks = make(map[int][]string) | |
f, err := os.OpenFile(filename, os.O_RDONLY, os.ModeAppend) | |
if err != nil { | |
return chunks, err | |
} | |
scanner := bufio.NewScanner(f) | |
reAnchor, err := regexp.Compile(anchorRegex) | |
if err != nil { | |
return chunks, err | |
} | |
var data []string | |
for scanner.Scan() { | |
data = append(data, scanner.Text()) | |
} | |
// first find the anchor index points | |
var anchors []int | |
for index, line := range data { | |
matchEntry := reAnchor.FindStringSubmatch(line) | |
if len(matchEntry) > 0 { | |
anchors = append(anchors, index) | |
} | |
} | |
// now walk through the data between anchor lines | |
for anchor, linenum := range anchors { | |
if (anchor + 1) > len(anchors)-1 { | |
// means we're at the last anchor and have to range differently | |
for i := linenum; i <= len(data)-1; i++ { | |
chunks[anchor] = append(chunks[anchor], data[i]) | |
} | |
} else { | |
for i := linenum; i <= anchors[anchor+1]-1; i++ { | |
chunks[anchor] = append(chunks[anchor], data[i]) | |
} | |
} | |
} | |
return chunks, err | |
} | |
func subExSingleFinder(line string, reOb *regexp.Regexp, matchGroup string) (resultSingle string, found bool) { | |
match := reOb.FindStringSubmatch(line) | |
found = false | |
if len(match) > 0 { | |
for i, name := range reOb.SubexpNames() { | |
if i != 0 { | |
if name == matchGroup { | |
resultSingle = match[i] | |
found = true | |
return resultSingle, found | |
} | |
} | |
} | |
} | |
return resultSingle, found | |
} | |
func loadProfileFile(filename string) (profiles []Profile, err error) { | |
reProfileRString := `^\[(?P<profilename>.*)\]` | |
chunks, err := readFileAnchorChunks(filename, reProfileRString) | |
if err != nil { | |
return profiles, err | |
} | |
for _, chunk := range chunks { | |
// now that we have a pre-separated chunk we can assume it's | |
// a new profile entry | |
var p Profile | |
for _, line := range chunk { | |
reProfile := regexp.MustCompile(reProfileRString) | |
reComment := regexp.MustCompile("^#(?P<comment>.*)") | |
reOutput := regexp.MustCompile("^output = (?P<output>.*)") | |
reRegion := regexp.MustCompile("^region = (?P<region>.*)") | |
reAKID := regexp.MustCompile("^aws_access_key_id = (?P<akid>.*)") | |
reSAK := regexp.MustCompile("^aws_secret_access_key = (?P<sak>.*)") | |
reST := regexp.MustCompile("^aws_session_token = (?P<st>.*)") | |
reRole := regexp.MustCompile(".*ASSUMED ROLE: (?P<rolearn>.*)") | |
matchComment := reComment.FindStringSubmatch(line) | |
if len(matchComment) > 0 { | |
p.Comments = append(p.Comments, matchComment[0]) | |
} | |
result, found := subExSingleFinder(line, reProfile, "profilename") | |
if found { | |
p.ProfileName = result | |
} | |
result, found = subExSingleFinder(line, reOutput, "output") | |
if found { | |
p.Output = result | |
} | |
result, found = subExSingleFinder(line, reRegion, "region") | |
if found { | |
p.Region = result | |
} | |
result, found = subExSingleFinder(line, reAKID, "akid") | |
if found { | |
p.AWSAccessKeyID = result | |
} | |
result, found = subExSingleFinder(line, reSAK, "sak") | |
if found { | |
p.AWSSecretAccessKey = result | |
} | |
result, found = subExSingleFinder(line, reST, "st") | |
if found { | |
p.AWSSessionToken = result | |
} | |
result, found = subExSingleFinder(line, reRole, "rolearn") | |
if found { | |
p.RoleArn = result | |
} | |
} | |
profiles = append(profiles, p) | |
} | |
return profiles, err | |
} | |
// Profile is an object to hold a parsed profile | |
// from an aws creds file | |
type Profile struct { | |
ProfileName string | |
Comments []string | |
Output string | |
Region string | |
AWSAccessKeyID string | |
AWSSecretAccessKey string | |
AWSSessionToken string | |
RoleArn string | |
} | |
func main() { | |
profiles, err := loadProfileFile("zSampleInput.txt") | |
if err != nil { | |
panic(err) | |
} | |
b, err := json.MarshalIndent(profiles, "", " ") | |
if err != nil { | |
panic(err) | |
} | |
fmt.Println(string(b)) | |
} |
[default] | |
output = json | |
region = us-east-1 | |
aws_access_key_id = BKIAIABOIDNONBVL987 | |
aws_secret_access_key = 78GisADFEk+knqq2ceGRYRTEERVVIumv7ZW | |
[dis-other-boi] | |
# This is that other account | |
output = json | |
region = us-east-1 | |
aws_access_key_id = ASIAIOIOVNOIWUEUFYA | |
aws_secret_access_key = vJVEREWB4Kaw/5Pl0DKWho+g1EfEFFDI0IOnw | |
aws_session_token = FQoDYXdzEDcaDPwyDDGStuhYrh6KBSLwAVJnpsF+36G9r2PCfT5KZNjVfLw7qXbnd6c1U0ZsV5+3rni/KSW8/5gW2ZkdN1gkO0ERnevergonnagiveyouupRoeqo4K7ycMlqibKo07An+fXjNx4wkTMTrceG3cE8zGeTL5LRJQh/GkoHFFFzD0DSuyFZJjXz56idF+HTi89yQH9NVCLvZz8KVyluvxObjfQW15QE0Yjl+gSFOnsqQe3iCPrOMWYkyyqPFDOtypsFcGG0KlreuycuGQPEub10n0xcu7kajotoRhQBZ3jkmO2Wk2RCjb+a/SBQ== | |
[dat-old-account] | |
# DO NOT EDIT | |
# GOSSAMER MANAGED SECTION | |
# (Will be overwritten regularly) | |
#################################################### | |
# ASSUMED ROLE: arn:aws:iam::790812376562953:role/cs/p-operations | |
# ASSUMED FROM INSTANCE ROLE: NA | |
# GENERATED: 2018-01-02 16:42:52.513969397 -0500 EST | |
# EXPIRES@2018-01-02 22:42:51 +0000 UTC | |
output = json | |
region = us-west-2 | |
aws_access_key_id = ASIAI6TATMY2YCE65J5Q | |
aws_secret_access_key = 82HwcMyftGhZxf+KM6HHuaaVTzRsqTPyNi1OU62d | |
aws_session_token = FQoDYXdzEDcaDINKwUQ4q6ts9XWIGiLwARg+RZM4hzkf59giN5sufa/K9+TOvk6TufnKXBeu467sWQ2BI3xmK1SBrvn2ufundcFud2LtyogO0lGyv8HJfJMc+BFEH/mzhPjudf0wYjGO+d0lOEj2knwnevergonnaletyoudown7pArq/WorFu0om24+gyFgXgc3fzkSVAE/WxAK3gGa18V8Uljj3ZL8JLBRYvMHrDTxw+WbQJ35NBMQ0o2/5DfBiD25znIuYmDXXqPLMmSN7WRiH2QNaPIzG6OwKbPrByvyQyaRMzKZAvGxbiu/syjb+a/SBQ== | |
[dat-new-account] | |
# DO NOT EDIT | |
# GOSSAMER MANAGED SECTION | |
# (Will be overwritten regularly) | |
#################################################### | |
# ASSUMED ROLE: arn:aws:iam::7815123567953:role/cs/p-operations | |
# ASSUMED FROM INSTANCE ROLE: NA | |
# GENERATED: 2018-01-02 16:46:40.704783754 -0500 EST | |
# EXPIRES@2018-01-02 22:46:40 +0000 UTC | |
output = json | |
region = us-east-2 | |
aws_access_key_id = ASIAAFEFVWEFQWXS4KVQ | |
aws_secret_access_key = MepbtEPH4EwLEopopamvoieDYJtKjruVU6YbNc | |
aws_session_token = FQoDYXdzEDcaDO5bzx2bPd8bCTdMAiLwAdjWobiKpismc5ybVEhSLbe1b0ADV6TSAugnRB7LrrSTT0TNIIAD1LBUC7swTHsMo0wCiwvLVynLaBefDEjQsRyi/IW7niJnallbymyself4K+PJIl912G+YNMDSoYMlyqd885qhYUJMH7G04DPKZWL0x8v0hr2gk73XaIOxcAAIkz12TEjk5ro4Y1f4wjcqUFO15CVM0CXiyYNTcxVZ4/Nm/kSHYCT9CSM67NULGf2jm+Ng9xijA+6/SBQ== | |
[ | |
{ | |
"ProfileName": "default", | |
"Comments": null, | |
"Output": "json", | |
"Region": "us-east-1", | |
"AWSAccessKeyID": "BKIAIABOIDNONBVL987", | |
"AWSSecretAccessKey": "78GisADFEk+knqq2ceGRYRTEERVVIumv7ZW", | |
"AWSSessionToken": "", | |
"RoleArn": "" | |
}, | |
{ | |
"ProfileName": "dis-other-boi", | |
"Comments": [ | |
"# This is that other account" | |
], | |
"Output": "json", | |
"Region": "us-east-1", | |
"AWSAccessKeyID": "ASIAIOIOVNOIWUEUFYA", | |
"AWSSecretAccessKey": "vJVEREWB4Kaw/5Pl0DKWho+g1EfEFFDI0IOnw", | |
"AWSSessionToken": "FQoDYXdzEDcaDPwyDDGStuhYrh6KBSLwAVJnpsF+36G9r2PCfT5KZNjVfLw7qXbnd6c1U0ZsV5+3rni/KSW8/5gW2ZkdN1gkO0ERnevergonnagiveyouupRoeqo4K7ycMlqibKo07An+fXjNx4wkTMTrceG3cE8zGeTL5LRJQh/GkoHFFFzD0DSuyFZJjXz56idF+HTi89yQH9NVCLvZz8KVyluvxObjfQW15QE0Yjl+gSFOnsqQe3iCPrOMWYkyyqPFDOtypsFcGG0KlreuycuGQPEub10n0xcu7kajotoRhQBZ3jkmO2Wk2RCjb+a/SBQ==", | |
"RoleArn": "" | |
}, | |
{ | |
"ProfileName": "dat-old-account", | |
"Comments": [ | |
"# DO NOT EDIT", | |
"# GOSSAMER MANAGED SECTION", | |
"# (Will be overwritten regularly)", | |
"####################################################", | |
"# ASSUMED ROLE: arn:aws:iam::790812376562953:role/cs/p-operations", | |
"# ASSUMED FROM INSTANCE ROLE: NA", | |
"# GENERATED: 2018-01-02 16:42:52.513969397 -0500 EST", | |
"# EXPIRES@2018-01-02 22:42:51 +0000 UTC" | |
], | |
"Output": "json", | |
"Region": "us-west-2", | |
"AWSAccessKeyID": "ASIAI6TATMY2YCE65J5Q", | |
"AWSSecretAccessKey": "82HwcMyftGhZxf+KM6HHuaaVTzRsqTPyNi1OU62d", | |
"AWSSessionToken": "FQoDYXdzEDcaDINKwUQ4q6ts9XWIGiLwARg+RZM4hzkf59giN5sufa/K9+TOvk6TufnKXBeu467sWQ2BI3xmK1SBrvn2ufundcFud2LtyogO0lGyv8HJfJMc+BFEH/mzhPjudf0wYjGO+d0lOEj2knwnevergonnaletyoudown7pArq/WorFu0om24+gyFgXgc3fzkSVAE/WxAK3gGa18V8Uljj3ZL8JLBRYvMHrDTxw+WbQJ35NBMQ0o2/5DfBiD25znIuYmDXXqPLMmSN7WRiH2QNaPIzG6OwKbPrByvyQyaRMzKZAvGxbiu/syjb+a/SBQ==", | |
"RoleArn": "arn:aws:iam::790812376562953:role/cs/p-operations" | |
}, | |
{ | |
"ProfileName": "dat-new-account", | |
"Comments": [ | |
"# DO NOT EDIT", | |
"# GOSSAMER MANAGED SECTION", | |
"# (Will be overwritten regularly)", | |
"####################################################", | |
"# ASSUMED ROLE: arn:aws:iam::7815123567953:role/cs/p-operations", | |
"# ASSUMED FROM INSTANCE ROLE: NA", | |
"# GENERATED: 2018-01-02 16:46:40.704783754 -0500 EST", | |
"# EXPIRES@2018-01-02 22:46:40 +0000 UTC" | |
], | |
"Output": "json", | |
"Region": "us-east-2", | |
"AWSAccessKeyID": "ASIAAFEFVWEFQWXS4KVQ", | |
"AWSSecretAccessKey": "MepbtEPH4EwLEopopamvoieDYJtKjruVU6YbNc", | |
"AWSSessionToken": "FQoDYXdzEDcaDO5bzx2bPd8bCTdMAiLwAdjWobiKpismc5ybVEhSLbe1b0ADV6TSAugnRB7LrrSTT0TNIIAD1LBUC7swTHsMo0wCiwvLVynLaBefDEjQsRyi/IW7niJnallbymyself4K+PJIl912G+YNMDSoYMlyqd885qhYUJMH7G04DPKZWL0x8v0hr2gk73XaIOxcAAIkz12TEjk5ro4Y1f4wjcqUFO15CVM0CXiyYNTcxVZ4/Nm/kSHYCT9CSM67NULGf2jm+Ng9xijA+6/SBQ==", | |
"RoleArn": "arn:aws:iam::7815123567953:role/cs/p-operations" | |
} | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment