Skip to content

Commit

Permalink
Added aws-dump
Browse files Browse the repository at this point in the history
  • Loading branch information
hamstah committed Jan 7, 2019
1 parent 5c952a8 commit 7d5086d
Show file tree
Hide file tree
Showing 11 changed files with 900 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v6.2.0 (07/01/2019)

**New**

* Added `aws-dump`

## v6.1.0 (21/12/2018)

**New**
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Collection of tools to make working with AWS a bit easier without having to depe

| Tool | Overview |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| [aws-dump](aws/dump) | Dumps (a subset of) AWS resources to JSON and optionally check if they are in terraform state. |
| [iam-session](iam/session/) | Creates new IAM session with role assumption and MFA support. |
| [iam-public-keys](iam/public-ssh-keys) | Returns the public SSH keys of an IAM user. |
| [iam-auth-proxy](iam/auth-proxy) | Use IAM as identity provider for services. |
Expand Down
100 changes: 100 additions & 0 deletions aws/dump/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# aws-dump

Dumps AWS resources to JSON and optionally check if they are managed by Terraform

```
usage: aws-dump --accounts-config=ACCOUNTS-CONFIG --output=OUTPUT [<flags>]
Dump AWS resources
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
--assume-role-arn=ASSUME-ROLE-ARN
Role to assume
--assume-role-external-id=ASSUME-ROLE-EXTERNAL-ID
External ID of the role to assume
--assume-role-session-name=ASSUME-ROLE-SESSION-NAME
Role session name
--region=REGION AWS Region
--mfa-serial-number=MFA-SERIAL-NUMBER
MFA Serial Number
--mfa-token-code=MFA-TOKEN-CODE
MFA Token Code
-v, --version Display the version
-c, --accounts-config=ACCOUNTS-CONFIG
Configuration file with the accounts to list resources for.
-t, --terraform-backends-config=TERRAFORM-BACKENDS-CONFIG
Configuration file with the terraform backends to compare with.
-o, --output=OUTPUT Filename to store the results in.
--only-unmanaged Only return resources not managed by terraform.
```

## Supported resources

* EC2
* VPC
* Security Groups
* IAM (Does not include attachments)
* Users
* Access keys
* Roles
* Policies
* S3
* Buckets

## Configuration

### AWS Accounts

Create a JSON file with the following structure

```js
{
"accounts": [
{
"role_arn": "arn:aws:iam::123456789012:role/Role",
"regions": ["us-east-1", "us-east-2", "eu-west-1"]
},
{
"role_arn": "arn:aws:iam::234567890123:role/Role",
"regions": ["us-east-1"]
}
]
}
```

Then pass the filename to the `--accounts-config` flag.

### Terraform

Currently only S3 backends are supported.

#### s3 backends

Create a JSON file with the following structure

```
{
"destination": "./terraform-states/",
"options": {
"path_substitutions": [
{
"old": "/",
"new": "-"
}
],
"overwrite": false
},
"s3":[
{
"bucket":"terraform-bucket",
"keys":[
"test.tfstate",
"prod.tfstate"
],
"region":"eu-west-1",
"role_arn":"arn:aws:iam::123456789012:role/Role"
}
]
}
```
70 changes: 70 additions & 0 deletions aws/dump/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"encoding/json"
"io/ioutil"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/hamstah/awstools/common"
)

type Account struct {
Regions []string `json:"regions"`
RoleARN string `json:"role_arn"`
ExternalID string `json:"external_id"`
SessionName string `json:"session_name"`
}

type Accounts struct {
Accounts []*Account `json:"accounts"`
Sessions []*Session
}

type Session struct {
Session *session.Session
Config *aws.Config
AccountID string
}

func NewAccounts(filename string) (*Accounts, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}

result := &Accounts{}
err = json.Unmarshal(data, result)
if err != nil {
return nil, err
}

for _, account := range result.Accounts {
for _, region := range account.Regions {
sess, conf := common.OpenSession(&common.SessionFlags{
RoleArn: &account.RoleARN,
RoleExternalID: &account.ExternalID,
Region: &region,
RoleSessionName: &account.SessionName,

MFASerialNumber: aws.String(""),
MFATokenCode: aws.String(""),
})

stsClient := sts.New(sess, conf)
identity, err := stsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{})
if err != nil {
return nil, err
}

result.Sessions = append(result.Sessions, &Session{
Session: sess,
Config: conf,
AccountID: *identity.Account,
})
}
}

return result, nil
}
69 changes: 69 additions & 0 deletions aws/dump/ec2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"fmt"

"github.com/aws/aws-sdk-go/service/ec2"
)

func EC2ListVpcs(session *Session) *FetchResult {
client := ec2.New(session.Session, session.Config)

vpcs := []Resource{}

res, err := client.DescribeVpcs(&ec2.DescribeVpcsInput{})
if err != nil {
return &FetchResult{nil, err}
}

for _, vpc := range res.Vpcs {
if *vpc.IsDefault {
continue
}
vpcs = append(vpcs, Resource{
ID: *vpc.VpcId,
// ARN
Service: "ec2",
Type: "vpc",
AccountID: *vpc.OwnerId,
Region: *session.Config.Region,
})
}

return &FetchResult{vpcs, err}
}

func EC2ListSecurityGroups(session *Session) *FetchResult {
client := ec2.New(session.Session, session.Config)

securityGroups := []Resource{}
err := client.DescribeSecurityGroupsPages(&ec2.DescribeSecurityGroupsInput{},
func(page *ec2.DescribeSecurityGroupsOutput, lastPage bool) bool {
for _, securityGroup := range page.SecurityGroups {
resource := Resource{
ID: *securityGroup.GroupId,
ARN: fmt.Sprintf("arn:aws:ec2:%s:%s:security-group/%s",
*session.Config.Region,
*securityGroup.OwnerId,
*securityGroup.GroupId,
),
Service: "ec2",
Type: "security-group",
AccountID: *securityGroup.OwnerId,
Region: *session.Config.Region,
Metadata: map[string]string{
"GroupName": *securityGroup.GroupName,
"Description": *securityGroup.Description,
},
}
if securityGroup.VpcId != nil {
resource.Metadata["VpcId"] = *securityGroup.VpcId
}
securityGroups = append(securityGroups, resource)
}

return true
})

return &FetchResult{securityGroups, err}
}
124 changes: 124 additions & 0 deletions aws/dump/iam.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
)

func IAMListUsersAndAccessKeys(session *Session) *FetchResult {
client := iam.New(session.Session, session.Config)

result := &FetchResult{}
result.Error = client.ListUsersPages(&iam.ListUsersInput{},
func(page *iam.ListUsersOutput, lastPage bool) bool {
for _, user := range page.Users {
resource, err := NewResource(*user.Arn)
if err != nil {
result.Error = err
return false
}
result.Resources = append(result.Resources, *resource)

keysResult := IAMListAccessKeys(session, *user.UserName)
if keysResult.Error != nil {
result.Error = keysResult.Error
return false
}
result.Resources = append(result.Resources, keysResult.Resources...)
}

return true
})

return result
}

func IAMListGroups(session *Session) *FetchResult {
client := iam.New(session.Session, session.Config)

result := &FetchResult{}
result.Error = client.ListGroupsPages(&iam.ListGroupsInput{},
func(page *iam.ListGroupsOutput, lastPage bool) bool {
for _, group := range page.Groups {

resource, err := NewResource(*group.Arn)
if err != nil {
result.Error = err
return false
}
result.Resources = append(result.Resources, *resource)
}

return true
})

return result
}

func IAMListRoles(session *Session) *FetchResult {
client := iam.New(session.Session, session.Config)

result := &FetchResult{}
result.Error = client.ListRolesPages(&iam.ListRolesInput{},
func(page *iam.ListRolesOutput, lastPage bool) bool {
for _, role := range page.Roles {
resource, err := NewResource(*role.Arn)
if err != nil {
result.Error = err
return false
}
result.Resources = append(result.Resources, *resource)
}

return true
})

return result
}

func IAMListPolicies(session *Session) *FetchResult {
client := iam.New(session.Session, session.Config)

result := &FetchResult{}
result.Error = client.ListPoliciesPages(&iam.ListPoliciesInput{Scope: aws.String("Local")},
func(page *iam.ListPoliciesOutput, lastPage bool) bool {
for _, policy := range page.Policies {
resource, err := NewResource(*policy.Arn)
if err != nil {
result.Error = err
return false
}
result.Resources = append(result.Resources, *resource)
}

return true
})

return result
}

func IAMListAccessKeys(session *Session, username string) *FetchResult {
client := iam.New(session.Session, session.Config)

result := &FetchResult{}
result.Error = client.ListAccessKeysPages(&iam.ListAccessKeysInput{
UserName: aws.String(username),
},
func(page *iam.ListAccessKeysOutput, lastPage bool) bool {
for _, accessKey := range page.AccessKeyMetadata {
result.Resources = append(result.Resources, Resource{
ID: *accessKey.AccessKeyId,
AccountID: session.AccountID,
Service: "iam",
Type: "access-key",
Metadata: map[string]string{
"UserName": *accessKey.UserName,
},
})
}

return true
})

return result
}
Loading

0 comments on commit 7d5086d

Please sign in to comment.