// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. // Package ssmparameterresolver provides helper methods to detect, validate and extract parameter store parameter references. package ssmparameterresolver import ( "errors" "regexp" "strings" "github.com/aws/amazon-ssm-agent/agent/log" ) // TextContainsSsmParameters determines whether a given text contains a pattern for SSM parameters func TextContainsSsmParameters(input string) bool { return ssmParameterPlaceholderRegEx.FindAllString(input, 1) != nil } // TextContainsSsmParameters determines whether a given text contains a pattern for secure SSM parameters func TextContainsSecureSsmParameters(input string) bool { return secureSsmParameterPlaceholderRegEx.FindAllString(input, 1) != nil } // ExtractParametersFromText takes text document and resolves all parameters in it according to ResolveOptions. // It will return a map of (parameter references) to SsmParameterInfo. func ExtractParametersFromText( service ISsmParameterService, log log.T, input string, options ResolveOptions) (map[string]SsmParameterInfo, error) { uniqueParameterReferences, err := parseParametersFromTextIntoDedupedSlice(input, options.IgnoreSecureParameters) if err != nil { return nil, err } parametersWithValues, err := getParametersFromSsmParameterStore(service, log, uniqueParameterReferences) if err != nil { return nil, err } prefixValidationError := validateParameterReferencePrefix(¶metersWithValues) if prefixValidationError != nil { return nil, prefixValidationError } return parametersWithValues, nil } // ResolveParameterReferenceList takes a list of SSM parameter references, resolves them according to ResolveOptions and // returns a map of (parameter references) to SsmParameterInfo. func ResolveParameterReferenceList( service ISsmParameterService, log log.T, parameterReferences []string, options ResolveOptions) (map[string]SsmParameterInfo, error) { uniqueParameterReferences := dedupSlice(parameterReferences) parameterReferencesToResolve := []string{} if options.IgnoreSecureParameters { for _, ref := range uniqueParameterReferences { if strings.HasPrefix(ref, ssmNonSecurePrefix) { parameterReferencesToResolve = append(parameterReferencesToResolve, ref) } } } else { parameterReferencesToResolve = append(parameterReferencesToResolve, uniqueParameterReferences...) } parametersWithValues, err := getParametersFromSsmParameterStore(service, log, parameterReferencesToResolve) if err != nil { return nil, err } prefixValidationError := validateParameterReferencePrefix(¶metersWithValues) if prefixValidationError != nil { return nil, prefixValidationError } return parametersWithValues, nil } // ResolveParametersInText takes text document, resolves all parameters in it according to ResolveOptions // and returns resolved document. func ResolveParametersInText( service ISsmParameterService, log log.T, input string, options ResolveOptions) (string, error) { resolvedParametersMap, err := ExtractParametersFromText(service, log, input, options) if err != nil || resolvedParametersMap == nil || len(resolvedParametersMap) == 0 { return input, err } for ref, param := range resolvedParametersMap { var placeholder = regexp.MustCompile("{{\\s*" + ref + "\\s*}}") input = placeholder.ReplaceAllString(input, param.Value) } return input, nil } func validateParameterReferencePrefix(resolvedParametersMap *map[string]SsmParameterInfo) error { for key, value := range *resolvedParametersMap { if strings.HasPrefix(key, ssmSecurePrefix) && value.Type != secureStringType { return errors.New("secure prefix " + ssmSecurePrefix + " is used for a non-secure type " + value.Type) } if strings.HasPrefix(key, ssmNonSecurePrefix) && value.Type == secureStringType { return errors.New("non-secure prefix " + ssmNonSecurePrefix + " is used for a secure type " + value.Type) } } return nil } func dedupSlice(slice []string) []string { ht := map[string]bool{} for _, element := range slice { ht[element] = true } keys := make([]string, len(ht)) i := 0 for k := range ht { keys[i] = k i++ } return keys } func parseParametersFromTextIntoDedupedSlice(text string, ignoreSecureParameters bool) ([]string, error) { matchedPhrases := ssmParameterPlaceholderRegEx.FindAllStringSubmatch(text, -1) parameterNamesDeduped := make(map[string]bool) for i := 0; i < len(matchedPhrases); i++ { parameterNamesDeduped[matchedPhrases[i][1]] = true } if !ignoreSecureParameters { matchedSecurePhrases := secureSsmParameterPlaceholderRegEx.FindAllStringSubmatch(text, -1) for i := 0; i < len(matchedSecurePhrases); i++ { parameterNamesDeduped[matchedSecurePhrases[i][1]] = true } } result := []string{} for key := range parameterNamesDeduped { result = append(result, key) } return result, nil }