github.com/in4it/ecs-deploy@v0.0.42-0.20240508120354-ed77ff16df25/provider/ecs/paramstore.go (about)

     1  package ecs
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/aws/awserr"
    10  	"github.com/aws/aws-sdk-go/aws/session"
    11  	"github.com/aws/aws-sdk-go/service/ssm"
    12  	"github.com/in4it/ecs-deploy/service"
    13  	"github.com/in4it/ecs-deploy/util"
    14  	"github.com/juju/loggo"
    15  )
    16  
    17  // logging
    18  var paramstoreLogger = loggo.GetLogger("paramstore")
    19  
    20  // parameter type
    21  type Parameter struct {
    22  	Arn     string `json:"arn"`
    23  	Name    string `json:"name"`
    24  	Type    string `json:"type"`
    25  	Value   string `json:"value"`
    26  	Version int64  `json:"version"`
    27  }
    28  
    29  // Paramstore struct
    30  type Paramstore struct {
    31  	Parameters      map[string]Parameter
    32  	SsmAssumingRole *ssm.SSM
    33  }
    34  
    35  func (p *Paramstore) IsEnabled() bool {
    36  	if util.GetEnv("PARAMSTORE_ENABLED", "no") == "yes" {
    37  		return true
    38  	} else {
    39  		return false
    40  	}
    41  }
    42  
    43  func (p *Paramstore) GetPrefix() string {
    44  	if util.GetEnv("AWS_ENV_PATH", "") != "" {
    45  		return util.GetEnv("AWS_ENV_PATH", "")
    46  	} else {
    47  		if util.GetEnv("PARAMSTORE_PREFIX", "") == "" {
    48  			return ""
    49  		} else {
    50  			return "/" + util.GetEnv("PARAMSTORE_PREFIX", "") + "-" + util.GetEnv("AWS_ACCOUNT_ENV", "") + "/ecs-deploy/"
    51  		}
    52  	}
    53  }
    54  func (p *Paramstore) GetPrefixForService(serviceName string) string {
    55  	if util.GetEnv("PARAMSTORE_PREFIX", "") == "" {
    56  		return ""
    57  	} else {
    58  		return "/" + util.GetEnv("PARAMSTORE_PREFIX", "") + "-" + util.GetEnv("AWS_ACCOUNT_ENV", "") + "/" + serviceName + "/"
    59  	}
    60  }
    61  func (p *Paramstore) AssumeRole(roleArn, roleSessionName, prevCreds string) (string, error) {
    62  	iam := IAM{}
    63  	creds, jsonCreds, err := iam.AssumeRole(roleArn, roleSessionName, prevCreds)
    64  	if err != nil {
    65  		return "", err
    66  	}
    67  	// assume role
    68  	sess := session.Must(session.NewSession())
    69  	p.SsmAssumingRole = ssm.New(sess, &aws.Config{Credentials: creds})
    70  	if p.SsmAssumingRole == nil {
    71  		return "", errors.New("Could not assume role")
    72  	}
    73  	paramstoreLogger.Debugf("Assumed role %v with roleSessionName %v", roleArn, roleSessionName)
    74  
    75  	return jsonCreds, nil
    76  }
    77  func (p *Paramstore) GetParameters(prefix string, withDecryption bool) error {
    78  	var svc *ssm.SSM
    79  	p.Parameters = make(map[string]Parameter)
    80  	if prefix == "" {
    81  		// no valid prefix - parameter store not in use
    82  		return nil
    83  	}
    84  	if p.SsmAssumingRole == nil {
    85  		svc = ssm.New(session.New())
    86  	} else {
    87  		svc = p.SsmAssumingRole
    88  	}
    89  	input := &ssm.GetParametersByPathInput{
    90  		Path:           aws.String(prefix),
    91  		WithDecryption: aws.Bool(withDecryption),
    92  	}
    93  
    94  	pageNum := 0
    95  	err := svc.GetParametersByPathPages(input,
    96  		func(page *ssm.GetParametersByPathOutput, lastPage bool) bool {
    97  			pageNum++
    98  			for _, param := range page.Parameters {
    99  				var value string
   100  				paramName := strings.Replace(*param.Name, prefix, "", -1)
   101  				paramstoreLogger.Debugf("Read parameter: %v", paramName)
   102  				if withDecryption || *param.Type != "SecureString" {
   103  					value = *param.Value
   104  				} else {
   105  					value = "***"
   106  				}
   107  				p.Parameters[paramName] = Parameter{
   108  					Arn:     aws.StringValue(param.ARN),
   109  					Name:    aws.StringValue(param.Name),
   110  					Type:    aws.StringValue(param.Type),
   111  					Value:   value,
   112  					Version: aws.Int64Value(param.Version),
   113  				}
   114  			}
   115  			return pageNum <= 50 // 50 iterations max
   116  		})
   117  
   118  	if err != nil {
   119  		if aerr, ok := err.(awserr.Error); ok {
   120  			paramstoreLogger.Errorf(aerr.Error())
   121  		} else {
   122  			paramstoreLogger.Errorf(err.Error())
   123  		}
   124  		return err
   125  	}
   126  	return nil
   127  }
   128  func (p *Paramstore) GetParameterValue(name string) (*string, error) {
   129  	if param, ok := p.Parameters[name]; ok {
   130  		if param.Value != "" {
   131  			return &param.Value, nil
   132  		}
   133  	} else {
   134  		return nil, errors.New("Tried getParameterValue on parameter that doesn't exist: " + name)
   135  	}
   136  
   137  	// val not found, but does exist, retrieve
   138  	svc := ssm.New(session.New())
   139  	input := &ssm.GetParameterInput{
   140  		Name:           aws.String(p.GetPrefix() + name),
   141  		WithDecryption: aws.Bool(true),
   142  	}
   143  
   144  	result, err := svc.GetParameter(input)
   145  	if err != nil {
   146  		if aerr, ok := err.(awserr.Error); ok {
   147  			switch aerr.Code() {
   148  			case ssm.ErrCodeParameterNotFound:
   149  				paramstoreLogger.Errorf("%v: %v", ssm.ErrCodeParameterNotFound, aerr.Error())
   150  				return nil, errors.New("ParameterNotFound")
   151  			default:
   152  				paramstoreLogger.Errorf(aerr.Error())
   153  			}
   154  		} else {
   155  			paramstoreLogger.Errorf(err.Error())
   156  		}
   157  		return nil, err
   158  	}
   159  	return result.Parameter.Value, nil
   160  }
   161  
   162  func (p *Paramstore) GetParamstoreIAMPolicy(path string) string {
   163  	iam := IAM{}
   164  	err := iam.GetAccountId()
   165  	accountId := iam.AccountId
   166  	if err != nil {
   167  		accountId = ""
   168  	}
   169  	policy := `{
   170      "Version": "2012-10-17",
   171      "Statement": [
   172        {
   173          "Action": [
   174            "ssm:GetParameterHistory",
   175            "ssm:GetParameter",
   176            "ssm:GetParameters",
   177            "ssm:GetParametersByPath"
   178          ],
   179          "Resource": [
   180            "arn:aws:ssm:` + util.GetEnv("AWS_REGION", "") + `:` + accountId + `:parameter/` + util.GetEnv("PARAMSTORE_PREFIX", "") + `-` + util.GetEnv("AWS_ACCOUNT_ENV", "") + `/` + path + `/*"
   181          ],
   182          "Effect": "Allow"
   183        },
   184        {
   185          "Action": [
   186            "kms:Decrypt"
   187          ],
   188          "Resource": [
   189            "` + util.GetEnv("PARAMSTORE_KMS_ARN", "") + `"
   190          ],
   191          "Effect": "Allow"
   192        }
   193      ]
   194    }`
   195  	return policy
   196  }
   197  func (p *Paramstore) PutParameter(serviceName string, parameter service.DeployServiceParameter) (*int64, error) {
   198  	var svc *ssm.SSM
   199  	if p.SsmAssumingRole == nil {
   200  		svc = ssm.New(session.New())
   201  	} else {
   202  		svc = p.SsmAssumingRole
   203  	}
   204  
   205  	input := &ssm.PutParameterInput{
   206  		Name:      aws.String(p.GetPrefixForService(serviceName) + parameter.Name),
   207  		Value:     aws.String(parameter.Value),
   208  		Overwrite: aws.Bool(true),
   209  	}
   210  	if parameter.Encrypted {
   211  		input.SetType("SecureString")
   212  		input.SetKeyId(util.GetEnv("PARAMSTORE_KMS_ARN", ""))
   213  	} else {
   214  		input.SetType("String")
   215  	}
   216  
   217  	result, err := svc.PutParameter(input)
   218  	if err != nil {
   219  		if aerr, ok := err.(awserr.Error); ok {
   220  			paramstoreLogger.Errorf(aerr.Error())
   221  		} else {
   222  			paramstoreLogger.Errorf(err.Error())
   223  		}
   224  		return nil, err
   225  	}
   226  	return result.Version, nil
   227  }
   228  func (p *Paramstore) DeleteParameter(serviceName, parameter string) error {
   229  	var svc *ssm.SSM
   230  	if p.SsmAssumingRole == nil {
   231  		svc = ssm.New(session.New())
   232  	} else {
   233  		svc = p.SsmAssumingRole
   234  	}
   235  
   236  	input := &ssm.DeleteParameterInput{
   237  		Name: aws.String(p.GetPrefixForService(serviceName) + parameter),
   238  	}
   239  
   240  	_, err := svc.DeleteParameter(input)
   241  	if err != nil {
   242  		if aerr, ok := err.(awserr.Error); ok {
   243  			paramstoreLogger.Errorf(aerr.Error())
   244  		} else {
   245  			paramstoreLogger.Errorf(err.Error())
   246  		}
   247  		return err
   248  	}
   249  	return nil
   250  }
   251  
   252  func (p *Paramstore) Bootstrap(serviceName, prefix string, environment string, parameters []service.DeployServiceParameter) error {
   253  	os.Setenv("PARAMSTORE_PREFIX", prefix)
   254  	os.Setenv("AWS_ACCOUNT_ENV", environment)
   255  	for _, v := range parameters {
   256  		p.PutParameter(serviceName, v)
   257  	}
   258  	return nil
   259  }
   260  
   261  func (p *Paramstore) RetrieveKeys() error {
   262  	if p.IsEnabled() {
   263  		err := p.GetParameters(p.GetPrefix(), true)
   264  		if err != nil {
   265  			return err
   266  		}
   267  		for k, v := range p.Parameters {
   268  			os.Setenv(k, v.Value)
   269  		}
   270  	}
   271  	return nil
   272  }