mirror of
https://github.com/foomo/posh-providers.git
synced 2025-10-16 12:35:41 +00:00
fix(azure/az): support service principals
This commit is contained in:
parent
88d82327fb
commit
0c3397ceae
@ -2,6 +2,8 @@ package az
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/posh/pkg/log"
|
||||
@ -11,11 +13,21 @@ import (
|
||||
|
||||
func AuthChecker(ctx context.Context, l log.Logger) []check.Info {
|
||||
name := "Azure"
|
||||
out, err := shell.New(ctx, l, "az", "account", "list", "--output", "none").Quiet().CombinedOutput()
|
||||
out, err := shell.New(ctx, l, "az", "account", "list", "--output", "json").Quiet().CombinedOutput()
|
||||
if err != nil {
|
||||
return []check.Info{check.NewFailureInfo(name, "Error: "+err.Error())}
|
||||
} else if strings.Contains(string(out), "az login") {
|
||||
return []check.Info{check.NewNoteInfo(name, "Unauthenticated")}
|
||||
}
|
||||
return []check.Info{check.NewSuccessInfo(name, "Authenticated")}
|
||||
|
||||
var res []map[string]any
|
||||
note := "Authenticated"
|
||||
if err := json.Unmarshal(out, &res); err == nil {
|
||||
if len(res) > 0 && res[0]["user"] != nil {
|
||||
if user, ok := res[0]["user"].(map[string]any); ok {
|
||||
note += fmt.Sprintf(" as %s: %s", user["type"], user["name"])
|
||||
}
|
||||
}
|
||||
}
|
||||
return []check.Info{check.NewSuccessInfo(name, note)}
|
||||
}
|
||||
|
||||
@ -69,7 +69,14 @@ func NewCommand(l log.Logger, az *AZ, kubectl *kubectl.Kubectl, opts ...CommandO
|
||||
{
|
||||
Name: "login",
|
||||
Description: "Log in to Azure",
|
||||
Execute: inst.login,
|
||||
Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error {
|
||||
fs.Internal().String("service-principal", "", "Service principal to use for authentication")
|
||||
if err := fs.Internal().SetValues("service-principal", inst.az.Config().ServicePrincipalNames()...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Execute: inst.login,
|
||||
},
|
||||
{
|
||||
Name: "logout",
|
||||
@ -240,10 +247,34 @@ func (c *Command) exec(ctx context.Context, r *readline.Readline) error {
|
||||
|
||||
func (c *Command) login(ctx context.Context, r *readline.Readline) error {
|
||||
fs := r.FlagSets().Default()
|
||||
return shell.New(ctx, c.l, "az", "login",
|
||||
"--allow-no-subscriptions",
|
||||
"--tenant", c.az.Config().TenantID,
|
||||
).
|
||||
ifs := r.FlagSets().Internal()
|
||||
|
||||
servicePricipal, err := ifs.GetString("service-principal")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var args []string
|
||||
if servicePricipal != "" {
|
||||
sp, err := c.az.cfg.ServicePrincipal(servicePricipal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args = append(args,
|
||||
"--service-principal",
|
||||
"--username", sp.ClientID,
|
||||
"--password", sp.ClientSecret,
|
||||
"--tenant", sp.TenantID,
|
||||
)
|
||||
} else {
|
||||
args = append(args,
|
||||
"--allow-no-subscriptions",
|
||||
"--tenant", c.az.Config().TenantID,
|
||||
)
|
||||
}
|
||||
|
||||
return shell.New(ctx, c.l, "az", "login").
|
||||
Args(args...).
|
||||
Args(fs.Visited().Args()...).
|
||||
Args(r.AdditionalArgs()...).
|
||||
Args(r.AdditionalFlags()...).
|
||||
|
||||
@ -14,6 +14,17 @@ type Config struct {
|
||||
TenantID string `json:"tenantId" yaml:"tenantId"`
|
||||
// Subscription configurations
|
||||
Subscriptions map[string]Subscription `json:"subscriptions" yaml:"subscriptions"`
|
||||
// Authentication service principals
|
||||
ServicePrincipals map[string]ServicePrincipal `json:"servicePrincipals" yaml:"servicePrincipals"`
|
||||
}
|
||||
|
||||
type ServicePrincipal struct {
|
||||
// Tenant id
|
||||
TenantID string `json:"tenantId" yaml:"tenantId"`
|
||||
// Application client id
|
||||
ClientID string `json:"clientId" yaml:"clientId"`
|
||||
// Application password
|
||||
ClientSecret string `json:"clientSecret" yaml:"clientSecret"`
|
||||
}
|
||||
|
||||
func (c Config) Subscription(name string) (Subscription, error) {
|
||||
@ -29,3 +40,17 @@ func (c Config) SubscriptionNames() []string {
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (c Config) ServicePrincipal(name string) (ServicePrincipal, error) {
|
||||
value, ok := c.ServicePrincipals[name]
|
||||
if !ok {
|
||||
return ServicePrincipal{}, errors.Errorf("service principal not found: %s", name)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (c Config) ServicePrincipalNames() []string {
|
||||
keys := lo.Keys(c.ServicePrincipals)
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
# yaml-language-server: $schema=config.schema.json
|
||||
configPath: .posh/config/azure
|
||||
tenantId: xxxx-xx-xx-xx-xxxx
|
||||
servicePrincipals:
|
||||
dev:
|
||||
tenantId: xxxx-xx-xx-xx-xxxx
|
||||
clientId: xxxx-xx-xx-xx-xxxx
|
||||
clientSecret: xxxx-xx-xx-xx-xxxx
|
||||
subscriptions:
|
||||
development:
|
||||
name: xxxx-xx-xx-xx-xxxx
|
||||
|
||||
@ -104,7 +104,7 @@ func NewCommand(l log.Logger, az *az.AZ, op *onepassword.OnePassword, cache cach
|
||||
return nil
|
||||
},
|
||||
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||
be, err := inst.cfg.Backend(r.Args().At(0))
|
||||
backend, err := inst.cfg.Backend(r.Args().At(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -123,11 +123,11 @@ func NewCommand(l log.Logger, az *az.AZ, op *onepassword.OnePassword, cache cach
|
||||
storageArgs = strings.Trim(strings.Trim(storageArgs, "\""), "'")
|
||||
|
||||
// Create a new resource group
|
||||
inst.l.Info("creating resource group:", be.ResourceGroup)
|
||||
inst.l.Info("creating resource group:", backend.ResourceGroup)
|
||||
if err := shell.New(ctx, inst.l, "az", "group", "create").
|
||||
Args("--resource-group", be.ResourceGroup).
|
||||
Args("--subscription", be.Subscription).
|
||||
Args("--location", be.Location).
|
||||
Args("--resource-group", backend.ResourceGroup).
|
||||
Args("--subscription", backend.Subscription).
|
||||
Args("--location", backend.Location).
|
||||
Args(strings.Split(groupArgs, " ")...).
|
||||
Args(r.FlagSets().Default().Visited().Args()...).
|
||||
Run(); err != nil {
|
||||
@ -135,12 +135,12 @@ func NewCommand(l log.Logger, az *az.AZ, op *onepassword.OnePassword, cache cach
|
||||
}
|
||||
|
||||
// Create a new resource group
|
||||
inst.l.Info("creating storage account:", be.StorageAccount)
|
||||
inst.l.Info("creating storage account:", backend.StorageAccount)
|
||||
if err := shell.New(ctx, inst.l, "az", "storage", "account", "create").
|
||||
Args("--name", be.StorageAccount).
|
||||
Args("--resource-group", be.ResourceGroup).
|
||||
Args("--subscription", be.Subscription).
|
||||
Args("--location", be.Location).
|
||||
Args("--name", backend.StorageAccount).
|
||||
Args("--resource-group", backend.ResourceGroup).
|
||||
Args("--subscription", backend.Subscription).
|
||||
Args("--location", backend.Location).
|
||||
Args(strings.Split(storageArgs, " ")...).
|
||||
Args(r.FlagSets().Default().Visited().Args()...).
|
||||
Run(); err != nil {
|
||||
@ -148,23 +148,16 @@ func NewCommand(l log.Logger, az *az.AZ, op *onepassword.OnePassword, cache cach
|
||||
}
|
||||
|
||||
// retrieve storage key
|
||||
inst.l.Info("retrieving storage key")
|
||||
sk, err := shell.New(ctx, inst.l, "az", "storage", "account", "keys", "list").
|
||||
Args("--resource-group", be.ResourceGroup).
|
||||
Args("--subscription", be.Subscription).
|
||||
Args("--account-name", be.StorageAccount).
|
||||
Args("-o", "tsv", "--query", "'[0].value'").
|
||||
Output()
|
||||
storageAccountKey, err := inst.getStorageAccountKey(ctx, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sks := strings.ReplaceAll(strings.TrimSpace(string(sk)), "\n", "")
|
||||
|
||||
inst.l.Info("creating storage container:", be.Container)
|
||||
inst.l.Info("creating storage container:", backend.Container)
|
||||
return shell.New(ctx, inst.l, "az", "storage", "container", "create").
|
||||
Args("--account-name", be.StorageAccount).
|
||||
Args("--account-key", sks).
|
||||
Args("--name", be.Container).
|
||||
Args("--account-name", backend.StorageAccount).
|
||||
Args("--account-key", storageAccountKey).
|
||||
Args("--name", backend.Container).
|
||||
Run()
|
||||
},
|
||||
},
|
||||
@ -172,15 +165,21 @@ func NewCommand(l log.Logger, az *az.AZ, op *onepassword.OnePassword, cache cach
|
||||
Name: "login",
|
||||
Description: "Log into your object storage backend",
|
||||
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||
be, sks, err := inst.backendKey(ctx, r.Args().At(0))
|
||||
backend, err := inst.cfg.Backend(r.Args().At(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return shell.New(ctx, inst.l, "pulumi", "login", fmt.Sprintf("azblob://%s", be.Container)).
|
||||
Env("AZURE_STORAGE_ACCOUNT=" + be.StorageAccount).
|
||||
Env("AZURE_STORAGE_KEY=" + sks).
|
||||
Env("ARM_SUBSCRIPTION_ID=" + be.Subscription).
|
||||
storageAccountKey, err := inst.getStorageAccountKey(ctx, backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return shell.New(ctx, inst.l, "pulumi", "login", fmt.Sprintf("azblob://%s", backend.Container)).
|
||||
Env("AZURE_STORAGE_ACCOUNT=" + backend.StorageAccount).
|
||||
Env("AZURE_STORAGE_KEY=" + storageAccountKey).
|
||||
Env("ARM_SUBSCRIPTION_ID=" + backend.Subscription).
|
||||
Debug().
|
||||
Run()
|
||||
},
|
||||
},
|
||||
@ -503,7 +502,12 @@ func (c *Command) executeStack(ctx context.Context, r *readline.Readline) error
|
||||
proj := r.Args().At(2)
|
||||
stack := r.Args().At(3)
|
||||
|
||||
be, storageAccountKey, err := c.backendKey(ctx, e)
|
||||
be, err := c.cfg.Backend(r.Args().At(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storageAccountKey, err := c.getStorageAccountKey(ctx, be)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -551,12 +555,7 @@ func (c *Command) completeProjects(ctx context.Context, t tree.Root, r *readline
|
||||
}).([]goprompt.Suggest)
|
||||
}
|
||||
|
||||
func (c *Command) backendKey(ctx context.Context, env string) (Backend, string, error) {
|
||||
be, err := c.cfg.Backend(env)
|
||||
if err != nil {
|
||||
return Backend{}, "", err
|
||||
}
|
||||
|
||||
func (c *Command) getStorageAccountKey(ctx context.Context, be Backend) (string, error) {
|
||||
// retrieve storage key
|
||||
c.l.Info("retrieving storage key")
|
||||
sk, err := shell.New(ctx, c.l, "az", "storage", "account", "keys", "list").
|
||||
@ -566,10 +565,10 @@ func (c *Command) backendKey(ctx context.Context, env string) (Backend, string,
|
||||
Args("-o", "tsv", "--query", "'[0].value'").
|
||||
Output()
|
||||
if err != nil {
|
||||
return Backend{}, "", err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return be, strings.ReplaceAll(strings.TrimSpace(string(sk)), "\n", ""), nil
|
||||
return strings.ReplaceAll(strings.TrimSpace(string(sk)), "\n", ""), nil
|
||||
}
|
||||
|
||||
func (c *Command) completeStacks(ctx context.Context, t tree.Root, r *readline.Readline) []goprompt.Suggest {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user