Skip to content

Instantly share code, notes, and snippets.

@a-h
Created December 21, 2018 15:21
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 a-h/b83249884e6e66ced90a5a777dac22a1 to your computer and use it in GitHub Desktop.
Save a-h/b83249884e6e66ced90a5a777dac22a1 to your computer and use it in GitHub Desktop.
Circle CI AWS Credentials Cycling
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
)
var circleAPITokenFlag = flag.String("circle-api-token", "", "The Circle CI token from https://circleci.com/account/api")
type project string
type username string
var projectToUsername = map[project]username{
project("github_organisation_or_username/project"): username("aws_iam_username"),
project("infinityworks/website"): username("website-ci"),
}
func main() {
flag.Parse()
if *circleAPITokenFlag == "" {
log.Fatal("missing -circle-api-token flag")
}
conf := aws.NewConfig().WithRegion("eu-west-2")
sess := session.Must(session.NewSession(conf))
svc := iam.New(sess)
for p, u := range projectToUsername {
log.Printf("%s/%s: starting\n", p, u)
// List existing keys for user.
keysToRemove, err := svc.ListAccessKeys(&iam.ListAccessKeysInput{
UserName: aws.String(string(u)),
})
if err != nil {
log.Fatalf("%s/%s: error listing access keys: %v", p, u, err)
}
// Delete the old ones to make space.
for _, k := range keysToRemove.AccessKeyMetadata {
_, err = svc.DeleteAccessKey(&iam.DeleteAccessKeyInput{
UserName: aws.String(string(u)),
AccessKeyId: k.AccessKeyId,
})
if err != nil {
log.Printf("%s/%s: error deleting access key %s: %v", p, u, *k.AccessKeyId, err)
}
}
// Create a new one.
newKey, err := svc.CreateAccessKey(&iam.CreateAccessKeyInput{
UserName: aws.String(string(u)),
})
if err != nil {
log.Fatalf("%s/%s: error creating new access key: %v", p, u, err)
}
// Apply it to Circle CI.
err = updateCircleCIAWSCredentials(p, *circleAPITokenFlag, newKey.AccessKey)
if err != nil {
log.Fatalf("%s/%s: error updating Circle CI: %v", p, u, err)
}
}
log.Println("done")
}
func updateCircleCIAWSCredentials(p project, token string, key *iam.AccessKey) (err error) {
err = environmentPost(p, token, env{
Name: "AWS_ACCESS_KEY_ID",
Value: *key.AccessKeyId,
})
if err != nil {
return
}
err = environmentPost(p, token, env{
Name: "AWS_SECRET_ACCESS_KEY",
Value: *key.SecretAccessKey,
})
return
}
func environmentPost(p project, token string, e env) error {
u := fmt.Sprintf("https://circleci.com/api/v1.1/project/github/%s/envvar?circle-token=%s",
url.PathEscape(string(p)),
url.QueryEscape(token))
buf, err := json.Marshal(e)
if err != nil {
return err
}
resp, err := http.Post(u, "application/json", bytes.NewReader(buf))
if resp.StatusCode >= 300 {
bdy, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("circleci returned status %d: %s", resp.StatusCode, string(bdy))
}
return nil
}
type env struct {
Name string `json:"name"`
Value string `json:"value"`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment