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  }