Skip to content

Instantly share code, notes, and snippets.

@rendicott
Last active January 3, 2018 13:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rendicott/7d511dc1caf6948178325f65631651c3 to your computer and use it in GitHub Desktop.
Save rendicott/7d511dc1caf6948178325f65631651c3 to your computer and use it in GitHub Desktop.
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