John

Senior Cloud Engineer & Technical Lead

Building a3s: How I Brought the k9s Experience to AWS IAM Management

Last week, I was knee-deep in debugging IAM role permissions for a Lambda function. I had fifteen browser tabs open - AWS Console, documentation, different role views - and I was constantly switching between them, waiting for pages to load, clicking through menus. The whole experience felt like swimming through molasses.

That’s when it hit me: why isn’t there something like k9s for AWS? If you’ve ever used k9s for Kubernetes, you know exactly what I mean - that beautiful, responsive terminal UI that makes navigating complex resources feel effortless. I wanted that same experience for AWS IAM. So I built it.

a3s Demo - Terminal UI for AWS IAM

The Problem: AWS Console Fatigue

Every cloud engineer knows this dance. You need to check an IAM role’s trust policy. Click, wait, load. Now you need to see its attached policies. Click, wait, load. Want to view the actual policy document? More clicks, more waiting. And don’t even get me started on searching through hundreds of roles to find the one you need.

The AWS CLI is powerful, but let’s be honest - nobody remembers this syntax off the top of their head:

aws iam list-roles --query 'Roles[?contains(RoleName,`lambda`)].{Name:RoleName,Arn:Arn}' --output table

And even if you do, parsing JSON output in your terminal isn’t exactly a pleasant experience. I found myself constantly piping to jq, losing context, and switching between terminal tabs just to understand a single role’s permissions.

Enter a3s: AWS Terminal User Interface

I decided to build a3s (pronounced “ace”) - a terminal-based user interface for AWS resources that brings the speed and efficiency of k9s to AWS management. The MVP focuses on IAM roles, but the architecture is designed to expand to other AWS services.

Here’s what makes a3s different:

Vim-Like Navigation That Just Works

j/k or ↑/↓     Navigate up/down
Enter          View role details
/              Search roles in real-time
g/G            Jump to top/bottom
Tab            Switch between tabs
Esc            Go back

No mouse needed. Your fingers never leave the home row. Every action is instant and responsive thanks to async loading - no more watching spinners while AWS APIs respond.

Real-Time Search That Actually Helps

Press / and start typing. The list filters instantly as you type. Looking for Lambda execution roles? Type “lambda” and watch the list update in real-time. No page refreshes, no waiting. It’s the search experience the AWS Console should have had from day one.

Tabbed Interface for Complete Role Information

When you drill into a role, you get everything organized in tabs:

  • Overview: Role ARN, creation date, last activity
  • Trust Policy: See who can assume this role
  • Policies: All attached managed and inline policies with an interactive JSON viewer
  • Tags: Resource tags and metadata

Each policy document is viewable in a scrollable, syntax-highlighted view. No more squinting at minified JSON or copying to an external editor.

The Technical Implementation

Building a3s taught me a lot about creating responsive TUIs in Go. Here’s the stack I chose and why:

Bubble Tea + Lipgloss: The Perfect TUI Combo

Bubble Tea provides the Model-View-Update architecture that makes complex UI state manageable. Every user action triggers an update, the model changes, and the view re-renders. It’s like React for the terminal, but the update patterns are more explicit:

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "/":
            m.searchMode = true
            return m, textinput.Blink
        case "enter":
            if m.searchMode {
                return m, m.performSearch()
            }
            return m, m.loadRoleDetails()
        }
    case roleLoadedMsg:
        m.roles = msg.roles
        m.loading = false
    }
    return m, nil
}

Lipgloss handles all the styling. Creating that k9s-inspired look with consistent colors, borders, and layouts was surprisingly straightforward:

var (
    headerStyle = lipgloss.NewStyle().
        Bold(true).
        Foreground(lipgloss.Color("#FFD700")).
        Background(lipgloss.Color("#1e1e1e")).
        Padding(0, 1)
    
    selectedStyle = lipgloss.NewStyle().
        Foreground(lipgloss.Color("#FFFFFF")).
        Background(lipgloss.Color("#5D4E75"))
)

AWS SDK v2: Fast and Async

The AWS SDK for Go v2 provides excellent support for concurrent API calls. When you open a role’s detail view, a3s fires off parallel requests for the trust policy, attached policies, inline policies, and tags. Each request updates the UI as it completes, so you see data immediately instead of waiting for everything to load.

State Management That Scales

The architecture separates concerns cleanly, using Bubble Tea’s Model-View-Update pattern:

graph TD A[a3s CLI] --> B[Main Model] B --> C[List Model] B --> D[Detail Model] C --> E[AWS Service Layer] D --> E E --> F[IAM ListRoles API] E --> G[IAM GetRole API] E --> H[IAM ListAttachedRolePolicies API] E --> I[IAM GetRolePolicy API] C --> J[Table View] C --> K[Search Filter] D --> L[Overview Tab] D --> M[Trust Policy Tab] D --> N[Policies Tab] D --> O[Tags Tab] style B fill:#2d3748,stroke:#4a5568,color:#fff style E fill:#1a365d,stroke:#2c5282,color:#fff style C fill:#2a4365,stroke:#3182ce,color:#fff style D fill:#2a4365,stroke:#3182ce,color:#fff

Each component has a single responsibility:

  • List Model: Manages the table of roles, search filtering, and navigation
  • Detail Model: Handles the tabbed interface and policy viewing
  • AWS Service: Abstracts all AWS API calls with proper error handling and concurrent loading

This separation makes adding new resources straightforward. Want to add EC2 instance management? Create a new model, wire up the AWS calls, and the UI framework handles the rest.

What’s Next: The Roadmap

The IAM role viewer is just the beginning. Here’s what’s coming:

Phase 2: Enhanced IAM

  • User and group management
  • Policy creation and editing
  • Permission boundary visualization

Phase 3: Core AWS Services

  • EC2 instances and security groups
  • Lambda functions with log viewing
  • S3 bucket browsing

Phase 4: Advanced Features

  • Multi-account support
  • Resource relationship graphs
  • CloudFormation stack visualization

Try It Yourself

Installing a3s is simple if you have Go installed:

git clone https://github.com/johnoct/a3s.git
cd a3s
go build -o a3s cmd/a3s/main.go
sudo mv a3s /usr/local/bin/

Then just run a3s in your terminal. It’ll use your default AWS profile and region, or you can specify them:

a3s -profile production -region us-west-2

Key Learnings

  • Bubble Tea’s message pattern is powerful: The Cmd return type lets you trigger async operations that send messages back to your model, enabling responsive UIs without blocking
  • Concurrent AWS API calls transform the experience: Using sync.WaitGroup and goroutines to fetch role policies in parallel reduced load times from 3-5 seconds to under 1 second
  • Terminal styling libraries have matured: Lipgloss makes complex layouts trivial - you can create bordered, padded, colored components with method chaining
  • Vim keybindings are non-negotiable for developer tools: Supporting hjkl navigation, / for search, and gg/G for jumping makes adoption instant for terminal users
  • Local tools beat web interfaces for speed: Zero network latency for UI interactions, combined with efficient API batching, creates a fundamentally better experience than clicking through AWS Console
  • Architecture matters from day one: Separating UI models from AWS service logic made adding new resource types straightforward - the same patterns work for any AWS service