Skip to content

Instantly share code, notes, and snippets.

@alsmola
Last active February 10, 2021 21:37
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alsmola/58a0f729e0960208df20e1ad11fcf007 to your computer and use it in GitHub Desktop.
Save alsmola/58a0f729e0960208df20e1ad11fcf007 to your computer and use it in GitHub Desktop.
List Okta account access to AWS IAM roles
package main
import (
"context"
"encoding/csv"
"errors"
"fmt"
"log"
"os"
"regexp"
"github.com/okta/okta-sdk-golang/v2/okta"
"github.com/okta/okta-sdk-golang/v2/okta/query"
)
// Map of Okta email to array of AWS IAM roles
type userRoles map[string][]string
func main() {
ctx := context.Background()
oktaOrgURL := "https://yoursubdomain.okta.com"
ctx, client, err := okta.NewClient(ctx, okta.WithOrgUrl(oktaOrgURL), okta.WithToken("your-okta-api-token"))
if err != nil {
panic(err)
}
applications, err := getOktaApps(ctx, client)
if err != nil {
panic(err)
}
awsAppID := ""
for _, app := range applications {
if app.Name == "amazon_aws" {
awsAppID = app.Id
}
}
if awsAppID == "" {
panic(errors.New("No AWS application found"))
}
appUsers, err := getOktaUsersForAWS(ctx, client, awsAppID)
if err != nil {
panic(err)
}
awsAccounts := []string{
"your-aws-account-names",
}
writers := map[string]*csv.Writer{}
for _, awsAccount := range awsAccounts {
file, err := os.Create(fmt.Sprintf("%s.csv", awsAccount))
if err != nil {
panic(err)
}
writers[awsAccount] = csv.NewWriter(file)
}
accountsUsersRoles := map[string]userRoles{}
for email, roles := range appUsers {
for _, role := range roles {
roleRegex := regexp.MustCompile(`\[(.*)\]\s--\s(.*)`)
matches := roleRegex.FindStringSubmatch(role)
if len(matches) < 3 {
panic(fmt.Errorf("Couldn't parse role: %s", role))
}
account := matches[1]
roleName := matches[2]
r := accountsUsersRoles[account][email]
r = append(r, roleName)
if accountsUsersRoles[account] == nil {
accountsUsersRoles[account] = userRoles{}
}
accountsUsersRoles[account][email] = r
}
}
for account, ur := range accountsUsersRoles {
for email, roles := range ur {
line := []string{email}
line = append(line, roles...)
err := writers[account].Write(line)
if err != nil {
panic(err)
}
}
}
for _, w := range writers {
w.Flush()
}
log.Println("Files created.")
}
func getOktaUsersForAWS(ctx context.Context, client *okta.Client, appID string) (userRoles, error) {
users := map[string][]string{}
// Get users assigned directly to application
appUsers, resp, err := client.Application.ListApplicationUsers(ctx, appID, nil)
if err != nil {
return users, err
}
for resp.HasNextPage() {
var nextAppUsers []*okta.AppUser
resp, err = resp.Next(ctx, &nextAppUsers)
if err != nil {
return users, err
}
appUsers = append(appUsers, nextAppUsers...)
}
for _, appUser := range appUsers {
userProfile := appUser.Profile.(map[string]interface{})
email := userProfile["email"].(string)
roles := []string{}
for _, r := range userProfile["samlRoles"].([]interface{}) {
roles = append(roles, r.(string))
}
users[email] = roles
}
// Get users assigned to application via groups
groups, resp, err := client.Application.ListApplicationGroupAssignments(ctx, appID, nil)
if err != nil {
return users, err
}
for resp.HasNextPage() {
var nextGroups []*okta.ApplicationGroupAssignment
resp, err = resp.Next(ctx, &nextGroups)
if err != nil {
return users, err
}
groups = append(groups, nextGroups...)
}
var groupUsers []*okta.User
for _, group := range groups {
roles := []string{}
profile := group.Profile.(map[string]interface{})
samlRoles := profile["samlRoles"].([]interface{})
for _, r := range samlRoles {
roles = append(roles, r.(string))
}
groupUsers, resp, err = client.Group.ListGroupUsers(ctx, group.Id, nil)
if err != nil {
return users, err
}
for resp.HasNextPage() {
var nextGroupUsers []*okta.User
resp, err = resp.Next(ctx, &nextGroupUsers)
if err != nil {
return users, err
}
groupUsers = append(groupUsers, nextGroupUsers...)
}
for _, groupUser := range groupUsers {
userProfile := *(groupUser.Profile)
email := userProfile["email"].(string)
existingRoles, ok := users[email]
if ok {
users[email] = union(existingRoles, roles)
}
}
}
return users, err
}
func getOktaApps(ctx context.Context, client *okta.Client) ([]*okta.Application, error) {
applications := []*okta.Application{}
params := query.Params{
Filter: "status eq \"ACTIVE\"",
}
apps, resp, err := client.Application.ListApplications(ctx, &params)
if err != nil {
log.Println("Can't get apps")
return applications, err
}
applications = transformApplications(apps)
for resp.HasNextPage() {
var nextApps []*okta.Application
resp, err = resp.Next(ctx, &nextApps)
if err != nil {
return applications, err
}
applications = append(applications, nextApps...)
}
return applications, nil
}
func transformApplications(values []okta.App) []*okta.Application {
var tValues []*okta.Application
for _, v := range values {
tValues = append(tValues, v.(*okta.Application))
}
return tValues
}
func union(a, b []string) []string {
m := make(map[string]bool)
for _, item := range a {
m[item] = true
}
for _, item := range b {
if _, ok := m[item]; !ok {
a = append(a, item)
}
}
return a
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment