3131import org .apache .nifi .parameter .AbstractParameterProvider ;
3232import org .apache .nifi .parameter .Parameter ;
3333import org .apache .nifi .parameter .ParameterGroup ;
34+ import org .apache .nifi .parameter .ParameterTag ;
3435import org .apache .nifi .parameter .VerifiableParameterProvider ;
3536import org .apache .nifi .processor .util .StandardValidators ;
3637import org .apache .nifi .processors .aws .credentials .provider .AwsCredentialsProviderService ;
4546import software .amazon .awssdk .regions .Region ;
4647import software .amazon .awssdk .retries .DefaultRetryStrategy ;
4748import software .amazon .awssdk .services .secretsmanager .SecretsManagerClient ;
49+ import software .amazon .awssdk .services .secretsmanager .model .DescribeSecretRequest ;
50+ import software .amazon .awssdk .services .secretsmanager .model .DescribeSecretResponse ;
4851import software .amazon .awssdk .services .secretsmanager .model .GetSecretValueRequest ;
4952import software .amazon .awssdk .services .secretsmanager .model .GetSecretValueResponse ;
5053import software .amazon .awssdk .services .secretsmanager .model .ListSecretsRequest ;
5154import software .amazon .awssdk .services .secretsmanager .model .ListSecretsResponse ;
5255import software .amazon .awssdk .services .secretsmanager .model .ResourceNotFoundException ;
5356import software .amazon .awssdk .services .secretsmanager .model .SecretListEntry ;
5457import software .amazon .awssdk .services .secretsmanager .model .SecretsManagerException ;
58+ import software .amazon .awssdk .services .secretsmanager .model .Tag ;
5559
5660import java .time .Duration ;
5761import java .util .ArrayList ;
58- import java .util .Arrays ;
5962import java .util .Comparator ;
60- import java .util .HashSet ;
63+ import java .util .HashMap ;
6164import java .util .List ;
6265import java .util .Map ;
6366import java .util .Optional ;
64- import java .util .Set ;
6567import java .util .regex .Pattern ;
6668import javax .net .ssl .KeyManager ;
6769import javax .net .ssl .TrustManager ;
7779 "key/value pairs in the secret mapping to Parameters in the group." )
7880public class AwsSecretsManagerParameterProvider extends AbstractParameterProvider implements VerifiableParameterProvider {
7981 enum ListingStrategy implements DescribedValue {
80- ENUMERATION ("Enumerate Secret Names" , "Requires a set of secret names to fetch. AWS actions required: GetSecretValue." ),
82+ ENUMERATION ("Enumerate Secret Names" , "Requires a set of secret names to fetch. AWS actions required: DescribeSecret and GetSecretValue." ),
8183
8284 PATTERN ("Match Pattern" , "Requires a regular expression pattern to match secret names. AWS actions required: ListSecrets and GetSecretValue." );
8385
@@ -196,10 +198,16 @@ public List<ParameterGroup> fetchParameters(final ConfigurationContext context)
196198
197199 // Fetch either by pattern or by enumerated list. See description of SECRET_LISTING_STRATEGY for more details.
198200 final ListingStrategy listingStrategy = context .getProperty (SECRET_LISTING_STRATEGY ).asAllowableValue (ListingStrategy .class );
199- final Set <String > fetchSecretNames = new HashSet <>();
201+ final Map <String , List <Tag >> secretNameToTags = new HashMap <>();
202+
200203 if (listingStrategy == ListingStrategy .ENUMERATION ) {
201204 final String secretNames = context .getProperty (SECRET_NAMES ).getValue ();
202- fetchSecretNames .addAll (Arrays .asList (secretNames .split ("," )));
205+ for (final String secretName : secretNames .split ("," )) {
206+ final String trimmedName = secretName .trim ();
207+ // For enumeration strategy, we need to call DescribeSecret to get tags
208+ final List <Tag > tags = describeSecretTags (secretsManager , trimmedName );
209+ secretNameToTags .put (trimmedName , tags );
210+ }
203211 } else {
204212 final Pattern secretNamePattern = Pattern .compile (context .getProperty (SECRET_NAME_PATTERN ).getValue ());
205213
@@ -212,7 +220,9 @@ public List<ParameterGroup> fetchParameters(final ConfigurationContext context)
212220 getLogger ().debug ("Secret [{}] does not match the secret name pattern {}" , secretName , secretNamePattern );
213221 continue ;
214222 }
215- fetchSecretNames .add (secretName );
223+ // ListSecrets response includes tags, so we can use them directly
224+ final List <Tag > tags = entry .hasTags () ? entry .tags () : List .of ();
225+ secretNameToTags .put (secretName , tags );
216226 }
217227 final String nextToken = listSecretsResponse .nextToken ();
218228 if (nextToken == null ) {
@@ -223,8 +233,10 @@ public List<ParameterGroup> fetchParameters(final ConfigurationContext context)
223233 }
224234 }
225235
226- for (final String secretName : fetchSecretNames ) {
227- final List <ParameterGroup > secretParameterGroups = fetchSecret (secretsManager , secretName );
236+ for (final Map .Entry <String , List <Tag >> entry : secretNameToTags .entrySet ()) {
237+ final String secretName = entry .getKey ();
238+ final List <ParameterTag > parameterTags = convertTags (entry .getValue ());
239+ final List <ParameterGroup > secretParameterGroups = fetchSecret (secretsManager , secretName , parameterTags );
228240 groups .addAll (secretParameterGroups );
229241 }
230242 return groups ;
@@ -257,7 +269,8 @@ public List<ConfigVerificationResult> verify(final ConfigurationContext context,
257269 return results ;
258270 }
259271
260- private List <ParameterGroup > fetchSecret (final SecretsManagerClient secretsManager , final String secretName ) {
272+ private List <ParameterGroup > fetchSecret (final SecretsManagerClient secretsManager , final String secretName ,
273+ final List <ParameterTag > tags ) {
261274 final List <ParameterGroup > groups = new ArrayList <>();
262275
263276 final List <Parameter > parameters = new ArrayList <>();
@@ -289,7 +302,7 @@ private List<ParameterGroup> fetchSecret(final SecretsManagerClient secretsManag
289302 }
290303 final String parameterValue = valueNode .asText ();
291304
292- parameters .add (createParameter (parameterName , parameterValue ));
305+ parameters .add (createParameter (parameterName , parameterValue , tags ));
293306 }
294307
295308 groups .add (new ParameterGroup (secretName , parameters ));
@@ -302,14 +315,55 @@ private List<ParameterGroup> fetchSecret(final SecretsManagerClient secretsManag
302315 }
303316 }
304317
305- private Parameter createParameter (final String parameterName , final String parameterValue ) {
318+ private Parameter createParameter (final String parameterName , final String parameterValue ,
319+ final List <ParameterTag > tags ) {
306320 return new Parameter .Builder ()
307321 .name (parameterName )
308322 .value (parameterValue )
309323 .provided (true )
324+ .tags (tags )
310325 .build ();
311326 }
312327
328+ /**
329+ * Retrieves tags for a secret using DescribeSecret API.
330+ * This is needed for the ENUMERATION strategy since GetSecretValue does not return tags.
331+ *
332+ * @param secretsManager the Secrets Manager client
333+ * @param secretName the name of the secret
334+ * @return list of tags associated with the secret
335+ */
336+ private List <Tag > describeSecretTags (final SecretsManagerClient secretsManager , final String secretName ) {
337+ try {
338+ final DescribeSecretRequest describeRequest = DescribeSecretRequest .builder ()
339+ .secretId (secretName )
340+ .build ();
341+ final DescribeSecretResponse describeResponse = secretsManager .describeSecret (describeRequest );
342+ return describeResponse .hasTags () ? describeResponse .tags () : List .of ();
343+ } catch (final ResourceNotFoundException e ) {
344+ getLogger ().debug ("Secret [{}] not found when describing for tags" , secretName );
345+ return List .of ();
346+ } catch (final SecretsManagerException e ) {
347+ getLogger ().warn ("Error describing secret [{}] for tags: {}" , secretName , e .getMessage ());
348+ return List .of ();
349+ }
350+ }
351+
352+ /**
353+ * Converts AWS Secrets Manager Tags to NiFi ParameterTags.
354+ *
355+ * @param awsTags the AWS tags to convert
356+ * @return list of NiFi ParameterTag objects
357+ */
358+ private List <ParameterTag > convertTags (final List <Tag > awsTags ) {
359+ if (awsTags == null || awsTags .isEmpty ()) {
360+ return List .of ();
361+ }
362+ return awsTags .stream ()
363+ .map (tag -> new ParameterTag (tag .key (), tag .value ()))
364+ .toList ();
365+ }
366+
313367 private ClientOverrideConfiguration createConfiguration () {
314368 return ClientOverrideConfiguration .builder ()
315369 .retryStrategy (DefaultRetryStrategy .doNotRetry ())
0 commit comments