github.com/defang-io/defang/src@v0.0.0-20240505002154-bdf411911834/pkg/clouds/aws/secrets.go (about) 1 package aws 2 3 import ( 4 "context" 5 "errors" 6 "sort" 7 8 "github.com/aws/aws-sdk-go-v2/service/ssm" 9 "github.com/aws/aws-sdk-go-v2/service/ssm/types" 10 "github.com/aws/smithy-go/ptr" 11 ) 12 13 // TODO: this function is pretty useless, but it's here for consistency 14 func getSecretID(name string) *string { 15 return ptr.String(name) 16 } 17 18 func IsParameterNotFoundError(err error) bool { 19 var e *types.ParameterNotFound 20 return errors.As(err, &e) 21 } 22 23 func (a *Aws) DeleteSecrets(ctx context.Context, names ...string) error { 24 cfg, err := a.LoadConfig(ctx) 25 if err != nil { 26 return err 27 } 28 29 svc := ssm.NewFromConfig(cfg) 30 31 o, err := svc.DeleteParameters(ctx, &ssm.DeleteParametersInput{ 32 Names: names, // works because getSecretID is a no-op 33 }) 34 if err != nil { 35 return err 36 } 37 if len(o.InvalidParameters) > 0 && len(o.DeletedParameters) == 0 { 38 return &types.ParameterNotFound{} 39 } 40 return nil 41 } 42 43 func (a *Aws) IsValidSecret(ctx context.Context, name string) (bool, error) { 44 cfg, err := a.LoadConfig(ctx) 45 if err != nil { 46 return false, err 47 } 48 49 secretId := getSecretID(name) 50 51 svc := ssm.NewFromConfig(cfg) 52 53 res, err := svc.DescribeParameters(ctx, &ssm.DescribeParametersInput{ 54 MaxResults: ptr.Int32(1), 55 ParameterFilters: []types.ParameterStringFilter{ 56 { 57 Key: ptr.String("Name"), 58 Option: ptr.String("Equals"), 59 Values: []string{*secretId}, 60 }, 61 }, 62 }) 63 if err != nil { 64 return false, err 65 } 66 return len(res.Parameters) == 1, nil 67 } 68 69 func (a *Aws) PutSecret(ctx context.Context, name, value string) error { 70 cfg, err := a.LoadConfig(ctx) 71 if err != nil { 72 return err 73 } 74 75 secretId := getSecretID(name) 76 secretString := ptr.String(value) 77 78 svc := ssm.NewFromConfig(cfg) 79 80 // Call ssm:PutParameter 81 _, err = svc.PutParameter(ctx, &ssm.PutParameterInput{ 82 Overwrite: ptr.Bool(true), 83 Type: types.ParameterTypeSecureString, 84 Name: secretId, 85 Value: secretString, 86 // SecretString: secretString, 87 }) 88 if err != nil { 89 return err 90 } 91 return nil 92 } 93 94 func (a *Aws) ListSecrets(ctx context.Context) ([]string, error) { 95 return a.ListSecretsByPrefix(ctx, "") 96 } 97 98 func (a *Aws) ListSecretsByPrefix(ctx context.Context, prefix string) ([]string, error) { 99 cfg, err := a.LoadConfig(ctx) 100 if err != nil { 101 return nil, err 102 } 103 104 svc := ssm.NewFromConfig(cfg) 105 106 var filters []types.ParameterStringFilter 107 // DescribeParameters fails if the BeginsWith value is empty 108 if prefix := *getSecretID(prefix); prefix != "" { 109 filters = append(filters, types.ParameterStringFilter{ 110 Key: ptr.String("Name"), 111 Option: ptr.String("BeginsWith"), 112 Values: []string{prefix}, 113 }) 114 } 115 116 res, err := svc.DescribeParameters(ctx, &ssm.DescribeParametersInput{ 117 // MaxResults: ptr.Int64(10); TODO: limit the output depending on quotas 118 ParameterFilters: filters, 119 }) 120 if err != nil { 121 return nil, err 122 } 123 124 names := make([]string, 0, len(res.Parameters)) 125 for _, p := range res.Parameters { 126 names = append(names, *p.Name) 127 } 128 sort.Strings(names) // make sure the output is deterministic 129 return names, nil 130 }