Skip to content

Getting started

By the end of this you will have one AWS credential resolution feeding two service clients, and you will have seen that constructing the source does no work at all.

go get gitlab.com/phpboyscout/go/awsclient

1. Build a source

package main

import (
    "context"
    "fmt"

    "github.com/aws/aws-sdk-go-v2/service/s3"
    "github.com/aws/aws-sdk-go-v2/service/ssm"
    "gitlab.com/phpboyscout/go/awsclient"
)

func main() {
    src := awsclient.Ambient(awsclient.WithRegion("eu-west-2"))

    fmt.Println("source built; nothing resolved yet")

    cfg, err := src.AWSConfig(context.Background())
    if err != nil {
        panic(err)
    }

    ssmClient := ssm.NewFromConfig(cfg)
    s3Client := s3.NewFromConfig(cfg)

    fmt.Println("two clients, one credential resolution")
    _, _ = ssmClient, s3Client
}

Ambient returns immediately and performs no I/O. The chain is resolved on the first AWSConfig call — which is what lets you build a source in a constructor that has no context to hand, or in package init.

2. Prove the sharing

Ask twice and the second call does not re-resolve:

    cfg1, _ := src.AWSConfig(ctx)
    cfg2, _ := src.AWSConfig(ctx)

Both calls return the same resolved configuration. Concurrent callers collapse into a single attempt rather than each starting their own — which matters when the chain reaches an instance metadata service that rate-limits.

3. Forget the region and see what happens

    src := awsclient.Ambient()          // no region
    _, err := src.AWSConfig(ctx)
    fmt.Println(err)
no AWS region configured; set one on the config, pass WithRegion, or set AWS_REGION

It refuses rather than guessing. A guessed region means calling a key in an account nobody named — so the module declines, and you decide.

You can also let the environment supply it:

export AWS_REGION=eu-west-2

4. Try again after a failure

Take the region away, ask, fail — then set it and ask again. The second call succeeds. Failures are never cached, so a credential chain that was not ready when your process started is picked up by the next attempt rather than requiring a restart.

That behaviour comes from go/clientlifecycle, which holds the state machine every provider module here shares.

Next