This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 3987d39cdc NIFI-9401 Added HashiCorpVaultParameterProvider
3987d39cdc is described below

commit 3987d39cdc9a9b6890df441edd134a5649a85548
Author: Joe Gresock <[email protected]>
AuthorDate: Fri Sep 9 14:19:46 2022 -0400

    NIFI-9401 Added HashiCorpVaultParameterProvider
    
    - Refactored nifi-vault-utils to nifi-hashicorp-vault-api and 
nifi-hashcorp-vault modules
    - Added HashiCorpVaultClientService and Standard implementation
    
    This closes #6304
    
    Signed-off-by: David Handermann <[email protected]>
---
 nifi-assembly/pom.xml                              |   6 +
 nifi-commons/nifi-hashicorp-vault-api/pom.xml      |  24 +++
 .../HashiCorpVaultCommunicationService.java        |  12 ++
 .../pom.xml                                        |   8 +-
 .../HashiCorpVaultConfigurationException.java      |   0
 ...StandardHashiCorpVaultCommunicationService.java |  13 ++
 .../config/HashiCorpVaultApplicationContext.java   |   0
 .../config/HashiCorpVaultConfiguration.java        |  35 +++-
 .../hashicorp/config/HashiCorpVaultProperties.java |   0
 .../hashicorp/config/HashiCorpVaultProperty.java   |   0
 .../config/HashiCorpVaultPropertySource.java       |   0
 .../config/HashiCorpVaultSslProperties.java        |   0
 .../config/lookup/BeanPropertyLookup.java          |   0
 .../hashicorp/config/lookup/PropertyLookup.java    |   0
 .../config/lookup/ValuePropertyLookup.java         |   0
 ...andardHashiCorpVaultCommunicationServiceIT.java |   0
 .../hashicorp/TestHashiCorpVaultConfiguration.java |   0
 ...StandardHashiCorpVaultCommunicationService.java |  10 +-
 .../nifi-property-protection-hashicorp/pom.xml     |   7 +-
 nifi-commons/pom.xml                               |   3 +-
 .../pom.xml                                        |  11 +-
 .../pom.xml                                        |  20 +-
 .../hashicorp/HashiCorpVaultClientService.java     | 115 +++++++++++
 .../vault/hashicorp/VaultAuthenticationMethod.java |  23 +--
 .../pom.xml                                        |  25 ++-
 .../StandardHashiCorpVaultClientService.java       | 209 +++++++++++++++++++
 .../org.apache.nifi.controller.ControllerService   |  15 ++
 .../additionalDetails.html                         | 133 ++++++++++++
 .../TestStandardHashiCorpVaultClientService.java   | 225 +++++++++++++++++++++
 .../nifi-hashicorp-vault-nar/pom.xml               |  22 ++
 .../pom.xml                                        |  23 ++-
 .../hashicorp/HashiCorpVaultParameterProvider.java | 163 +++++++++++++++
 .../org.apache.nifi.parameter.ParameterProvider    |  15 ++
 .../TestHashiCorpVaultParameterProvider.java       | 162 +++++++++++++++
 .../pom.xml                                        |   8 +-
 .../nifi-hashicorp-vault-bundle/pom.xml            |   4 +
 36 files changed, 1241 insertions(+), 50 deletions(-)

diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml
index c07683a50e..0baa9445d5 100644
--- a/nifi-assembly/pom.xml
+++ b/nifi-assembly/pom.xml
@@ -838,6 +838,12 @@ language governing permissions and limitations under the 
License. -->
             <version>1.18.0-SNAPSHOT</version>
             <type>nar</type>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            
<artifactId>nifi-hashicorp-vault-client-service-api-nar</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <type>nar</type>
+        </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-stateless-processor-nar</artifactId>
diff --git a/nifi-commons/nifi-hashicorp-vault-api/pom.xml 
b/nifi-commons/nifi-hashicorp-vault-api/pom.xml
new file mode 100644
index 0000000000..6cbec0d5a1
--- /dev/null
+++ b/nifi-commons/nifi-hashicorp-vault-api/pom.xml
@@ -0,0 +1,24 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <!--
+      Licensed to the Apache Software Foundation (ASF) under one or more
+      contributor license agreements.  See the NOTICE file distributed with
+      this work for additional information regarding copyright ownership.
+      The ASF licenses this file to You under the Apache License, Version 2.0
+      (the "License"); you may not use this file except in compliance with
+      the License.  You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License 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.
+    -->
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-commons</artifactId>
+        <version>1.18.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-hashicorp-vault-api</artifactId>
+</project>
+
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
 
b/nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
similarity index 91%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
rename to 
nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
index 840db72ffa..3d06270f7b 100644
--- 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
+++ 
b/nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.vault.hashicorp;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
@@ -24,6 +25,10 @@ import java.util.Optional;
  * @see <a href="https://www.vaultproject.io/";>https://www.vaultproject.io/</a>
  */
 public interface HashiCorpVaultCommunicationService {
+    /**
+     * @return The HashiCorp Vault server version
+     */
+    String getServerVersion();
 
     /**
      * Encrypts the given plaintext using Vault's Transit Secrets Engine.
@@ -83,4 +88,11 @@ public interface HashiCorpVaultCommunicationService {
      * @return A map from key to value from the secret key/values, or an empty 
map if not found
      */
     Map<String, String> readKeyValueSecretMap(String keyValuePath, String 
secretKey);
+
+    /**
+     * Lists the secrets at the given Key/Value Secrets Engine path.
+     * @param keyValuePath The Vault path to list
+     * @return The list of secret names
+     */
+    List<String> listKeyValueSecrets(String keyValuePath);
 }
diff --git a/nifi-commons/nifi-vault-utils/pom.xml 
b/nifi-commons/nifi-hashicorp-vault/pom.xml
similarity index 91%
rename from nifi-commons/nifi-vault-utils/pom.xml
rename to nifi-commons/nifi-hashicorp-vault/pom.xml
index 11d39ba9a7..a012e2d3e8 100644
--- a/nifi-commons/nifi-vault-utils/pom.xml
+++ b/nifi-commons/nifi-hashicorp-vault/pom.xml
@@ -19,7 +19,7 @@
         <artifactId>nifi-commons</artifactId>
         <version>1.18.0-SNAPSHOT</version>
     </parent>
-    <artifactId>nifi-vault-utils</artifactId>
+    <artifactId>nifi-hashicorp-vault</artifactId>
     <properties>
         <spring.vault.version>2.3.2</spring.vault.version>
     </properties>
@@ -45,6 +45,12 @@
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-utils</artifactId>
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
similarity index 100%
copy from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
copy to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
similarity index 94%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
index a407b85eed..38a6e5dfc6 100644
--- 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
+++ 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
@@ -34,6 +34,7 @@ import org.springframework.vault.support.VaultResponseSupport;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -64,6 +65,11 @@ public class StandardHashiCorpVaultCommunicationService 
implements HashiCorpVaul
         keyValueOperationsMap = new HashMap<>();
     }
 
+    @Override
+    public String getServerVersion() {
+        return vaultTemplate.opsForSys().health().getVersion();
+    }
+
     /**
      * Creates a VaultCommunicationService that uses Spring Vault.
      * @param vaultProperties Properties to configure the service
@@ -136,6 +142,13 @@ public class StandardHashiCorpVaultCommunicationService 
implements HashiCorpVaul
         return response == null ? Collections.emptyMap() : (Map<String, 
String>) response.getRequiredData();
     }
 
+    @Override
+    public List<String> listKeyValueSecrets(final String keyValuePath) {
+        final VaultKeyValueOperations keyValueOperations = 
keyValueOperationsMap
+                .computeIfAbsent(keyValuePath, path -> 
vaultTemplate.opsForKeyValue(path, KeyValueBackend.KV_1));
+        return keyValueOperations.list("/");
+    }
+
     private static class SecretData {
         private final String value;
 
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultApplicationContext.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultApplicationContext.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultApplicationContext.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultApplicationContext.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
similarity index 81%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
index 873477c097..f20335eeb4 100644
--- 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
+++ 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
@@ -32,6 +32,7 @@ import org.springframework.vault.support.SslConfiguration;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.time.Duration;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -70,13 +71,14 @@ public class HashiCorpVaultConfiguration extends 
EnvironmentVaultConfiguration {
     private final KeyValueBackend keyValueBackend;
 
     /**
-     * Creates a HashiCorpVaultConfiguration from property sources
-     * @param propertySources A series of Spring PropertySource objects
+     * Creates a HashiCorpVaultConfiguration from property sources, in 
increasing precedence.
+     * @param propertySources A series of Spring PropertySource objects (the 
last in the list take precedence over
+     *                        sources earlier in the list)
      * @throws HashiCorpVaultConfigurationException If the authentication 
properties file could not be read
      */
     public HashiCorpVaultConfiguration(final PropertySource<?>... 
propertySources) {
         final ConfigurableEnvironment env = new StandardEnvironment();
-        for(final PropertySource<?> propertySource : propertySources) {
+        for (final PropertySource<?> propertySource : propertySources) {
             env.getPropertySources().addFirst(propertySource);
         }
 
@@ -105,6 +107,7 @@ public class HashiCorpVaultConfiguration extends 
EnvironmentVaultConfiguration {
             }
         }
         this.keyValueBackend = keyValueBackend;
+        validateProperties(env);
 
         this.setApplicationContext(new HashiCorpVaultApplicationContext(env));
 
@@ -114,6 +117,30 @@ public class HashiCorpVaultConfiguration extends 
EnvironmentVaultConfiguration {
         clientOptions = getClientOptions();
     }
 
+    private void validateProperties(final ConfigurableEnvironment environment) 
{
+        try {
+            final String vaultUri = 
Objects.requireNonNull(environment.getProperty(VaultConfigurationKey.URI.key),
+                    "Missing required property " + 
VaultConfigurationKey.URI.key);
+            if (vaultUri.startsWith(HTTPS)) {
+                requireSslProperty("vault.ssl.key-store", environment);
+                requireSslProperty("vault.ssl.key-store-password", 
environment);
+                requireSslProperty("vault.ssl.key-store-type", environment);
+                requireSslProperty("vault.ssl.trust-store", environment);
+                requireSslProperty("vault.ssl.trust-store-password", 
environment);
+                requireSslProperty("vault.ssl.trust-store-type", environment);
+            }
+        } catch (final NullPointerException e) {
+            // Rethrow as IllegalArgumentException
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+
+    }
+
+    private void requireSslProperty(final String propertyName, final 
ConfigurableEnvironment environment) {
+        Objects.requireNonNull(environment.getProperty(propertyName), 
propertyName + " is required with an https URI");
+    }
+
+
     public KeyValueBackend getKeyValueBackend() {
         return keyValueBackend;
     }
@@ -158,7 +185,7 @@ public class HashiCorpVaultConfiguration extends 
EnvironmentVaultConfiguration {
         return new ClientOptions(connectionTimeoutDuration, 
readTimeoutDuration);
     }
 
-    private static Duration getDuration(String formattedDuration) {
+    private static Duration getDuration(final String formattedDuration) {
         final double duration = 
FormatUtils.getPreciseTimeDuration(formattedDuration, TimeUnit.MILLISECONDS);
         return Duration.ofMillis(Double.valueOf(duration).longValue());
     }
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperty.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperty.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperty.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperty.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultPropertySource.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultPropertySource.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultPropertySource.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultPropertySource.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultSslProperties.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultSslProperties.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultSslProperties.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultSslProperties.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/PropertyLookup.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/PropertyLookup.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/PropertyLookup.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/PropertyLookup.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/ValuePropertyLookup.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/ValuePropertyLookup.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/ValuePropertyLookup.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/ValuePropertyLookup.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
 
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
 
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
similarity index 100%
rename from 
nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
diff --git 
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
 
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
similarity index 93%
rename from 
nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
rename to 
nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
index a24f13b28d..d4e931758c 100644
--- 
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
+++ 
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
@@ -68,8 +68,8 @@ public class TestStandardHashiCorpVaultCommunicationService {
     public void testBasicConfiguration() {
         this.configureService();
 
-        // Once to check if the URI is https, and once by VaultTemplate
-        Mockito.verify(properties, Mockito.times(2)).getUri();
+        // Once to check if the URI is https, once by VaultTemplate, and once 
to validate
+        Mockito.verify(properties, Mockito.times(3)).getUri();
 
         // Once to check if the property is set, and once to retrieve the value
         Mockito.verify(properties, 
Mockito.times(2)).getAuthPropertiesFilename();
@@ -85,8 +85,6 @@ public class TestStandardHashiCorpVaultCommunicationService {
         Mockito.verify(sslProperties, 
Mockito.times(numberOfTimes)).getTrustStore();
         Mockito.verify(sslProperties, 
Mockito.times(numberOfTimes)).getTrustStoreType();
         Mockito.verify(sslProperties, 
Mockito.times(numberOfTimes)).getTrustStorePassword();
-        Mockito.verify(sslProperties, 
Mockito.times(numberOfTimes)).getEnabledProtocols();
-        Mockito.verify(sslProperties, 
Mockito.times(numberOfTimes)).getEnabledCipherSuites();
     }
 
     @Test
@@ -113,6 +111,8 @@ public class TestStandardHashiCorpVaultCommunicationService 
{
         when(properties.getUri()).thenReturn(URI_VALUE.replace("http", 
"https"));
         this.configureService();
 
-        this.ensureTlsPropertiesAccessed(1);
+        this.ensureTlsPropertiesAccessed(2);
+        Mockito.verify(sslProperties, Mockito.times(1)).getEnabledProtocols();
+        Mockito.verify(sslProperties, 
Mockito.times(1)).getEnabledCipherSuites();
     }
 }
diff --git a/nifi-commons/nifi-property-protection-hashicorp/pom.xml 
b/nifi-commons/nifi-property-protection-hashicorp/pom.xml
index f6be9da744..9d9509c55e 100644
--- a/nifi-commons/nifi-property-protection-hashicorp/pom.xml
+++ b/nifi-commons/nifi-property-protection-hashicorp/pom.xml
@@ -29,7 +29,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-vault-utils</artifactId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
         <dependency>
diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml
index 3dcdf77ef1..c62949b580 100644
--- a/nifi-commons/pom.xml
+++ b/nifi-commons/pom.xml
@@ -64,7 +64,8 @@
         <module>nifi-socket-utils</module>
         <module>nifi-utils</module>
         <module>nifi-uuid5</module>
-        <module>nifi-vault-utils</module>
+        <module>nifi-hashicorp-vault</module>
+        <module>nifi-hashicorp-vault-api</module>
         <module>nifi-web-client</module>
         <module>nifi-web-client-api</module>
         <module>nifi-web-utils</module>
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api-nar/pom.xml
similarity index 80%
copy from 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml
copy to 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api-nar/pom.xml
index 511f1a008a..f855199967 100644
--- 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api-nar/pom.xml
@@ -22,7 +22,7 @@
         <version>1.18.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>nifi-hashicorp-vault-nar</artifactId>
+    <artifactId>nifi-hashicorp-vault-client-service-api-nar</artifactId>
     <packaging>nar</packaging>
     <properties>
         <maven.javadoc.skip>true</maven.javadoc.skip>
@@ -32,8 +32,15 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            
<artifactId>nifi-hashicorp-vault-parameter-value-provider</artifactId>
+            <artifactId>nifi-standard-services-api-nar</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <type>nar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault-client-service-api</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
     </dependencies>
+
 </project>
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/pom.xml
similarity index 81%
copy from 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
copy to 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/pom.xml
index b9cee8e038..7245b6c861 100755
--- 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/pom.xml
@@ -20,29 +20,33 @@
         <artifactId>nifi-hashicorp-vault-bundle</artifactId>
         <version>1.18.0-SNAPSHOT</version>
     </parent>
-    <artifactId>nifi-hashicorp-vault-parameter-value-provider</artifactId>
+    <artifactId>nifi-hashicorp-vault-client-service-api</artifactId>
     <packaging>jar</packaging>
     <dependencies>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-stateless-api</artifactId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-vault-utils</artifactId>
+            <artifactId>nifi-ssl-context-service-api</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
-        <!-- test dependencies -->
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-expression-language</artifactId>
+            <artifactId>nifi-utils</artifactId>
             <version>1.18.0-SNAPSHOT</version>
+        </dependency>
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
new file mode 100644
index 0000000000..4a7376a04d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.apache.nifi.vault.hashicorp;
+
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.resource.ResourceCardinality;
+import org.apache.nifi.components.resource.ResourceType;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.VerifiableControllerService;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.ssl.SSLContextService;
+
+/**
+ * Provides a HashiCorpVaultCommunicationService.
+ */
+public interface HashiCorpVaultClientService extends ControllerService, 
VerifiableControllerService {
+
+    AllowableValue DIRECT_PROPERTIES = new AllowableValue("direct-properties", 
"Direct Properties",
+            "Use properties, including dynamic properties, configured directly 
in the Controller Service to configure the client");
+    AllowableValue PROPERTIES_FILES = new AllowableValue("properties-files", 
"Properties Files",
+            "Use one or more '.properties' files to configure the client");
+
+    PropertyDescriptor CONFIGURATION_STRATEGY = new 
PropertyDescriptor.Builder()
+            .displayName("Configuration Strategy")
+            .name("configuration-strategy")
+            .required(true)
+            .allowableValues(DIRECT_PROPERTIES, PROPERTIES_FILES)
+            .defaultValue(DIRECT_PROPERTIES.getValue())
+            .description("Specifies the source of the configuration 
properties.")
+            .build();
+
+    PropertyDescriptor VAULT_URI = new PropertyDescriptor.Builder()
+            .name("vault.uri")
+            .displayName("Vault URI")
+            .description("The URI of the HashiCorp Vault server (e.g., 
http://localhost:8200).  Required if not specified in the " +
+                    "Bootstrap HashiCorp Vault Configuration File.")
+            .required(true)
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.URI_VALIDATOR)
+            .dependsOn(CONFIGURATION_STRATEGY, DIRECT_PROPERTIES)
+            .build();
+
+    PropertyDescriptor VAULT_AUTHENTICATION = new PropertyDescriptor.Builder()
+            .name("vault.authentication")
+            .displayName("Vault Authentication")
+            .description("Vault authentication method, as described in the 
Spring Vault Environment Configuration documentation " +
+                    
"(https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration).")
+            .required(true)
+            .allowableValues(VaultAuthenticationMethod.values())
+            .defaultValue(VaultAuthenticationMethod.TOKEN.name())
+            .dependsOn(CONFIGURATION_STRATEGY, DIRECT_PROPERTIES)
+            .build();
+
+    PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
+            .name("vault.ssl.context.service")
+            .displayName("SSL Context Service")
+            .description("The SSL Context Service used to provide client 
certificate information for TLS/SSL connections to the " +
+                    "HashiCorp Vault server.")
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .dependsOn(CONFIGURATION_STRATEGY, DIRECT_PROPERTIES)
+            .build();
+
+    PropertyDescriptor VAULT_PROPERTIES_FILES = new 
PropertyDescriptor.Builder()
+            .name("vault.properties.files")
+            .displayName("Vault Properties Files")
+            .description("A comma-separated list of files containing HashiCorp 
Vault configuration properties, as described in the Spring Vault " +
+                    "Environment Configuration documentation 
(https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration).
 " +
+                    "All of the Spring property keys and 
authentication-specific property keys are supported.")
+            .required(true)
+            .dependsOn(CONFIGURATION_STRATEGY, PROPERTIES_FILES)
+            .identifiesExternalResource(ResourceCardinality.MULTIPLE, 
ResourceType.FILE)
+            .build();
+
+    PropertyDescriptor CONNECTION_TIMEOUT = new PropertyDescriptor.Builder()
+            .name("vault.connection.timeout")
+            .displayName("Connection Timeout")
+            .description("The connection timeout for the HashiCorp Vault 
client")
+            .required(true)
+            .defaultValue("5 sec")
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    PropertyDescriptor READ_TIMEOUT = new PropertyDescriptor.Builder()
+            .name("vault.read.timeout")
+            .displayName("Read Timeout")
+            .description("The read timeout for the HashiCorp Vault client")
+            .required(true)
+            .defaultValue("15 sec")
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    /**
+     *
+     * @return A service for communicating with HashiCorp Vault.
+     */
+    HashiCorpVaultCommunicationService getHashiCorpVaultCommunicationService();
+
+}
diff --git 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/VaultAuthenticationMethod.java
similarity index 67%
rename from 
nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
rename to 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/VaultAuthenticationMethod.java
index 8948d433e6..075f0f99b5 100644
--- 
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultConfigurationException.java
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/VaultAuthenticationMethod.java
@@ -16,18 +16,13 @@
  */
 package org.apache.nifi.vault.hashicorp;
 
-/**
- * Indicates a misconfiguration of the Vault client.
- */
-public class HashiCorpVaultConfigurationException extends RuntimeException {
-    public HashiCorpVaultConfigurationException() {
-    }
-
-    public HashiCorpVaultConfigurationException(String message) {
-        super(message);
-    }
-
-    public HashiCorpVaultConfigurationException(String message, Throwable 
cause) {
-        super(message, cause);
-    }
+public enum VaultAuthenticationMethod {
+    TOKEN,
+    APPID,
+    APPROLE,
+    AWS_EC2,
+    AZURE,
+    CERT,
+    CUBBYHOLE,
+    KUBERNETES
 }
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/pom.xml
similarity index 71%
copy from 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
copy to 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/pom.xml
index b9cee8e038..6ed1f61677 100755
--- 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/pom.xml
@@ -15,24 +15,40 @@
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <modelVersion>4.0.0</modelVersion>
+
     <parent>
         <groupId>org.apache.nifi</groupId>
         <artifactId>nifi-hashicorp-vault-bundle</artifactId>
         <version>1.18.0-SNAPSHOT</version>
     </parent>
-    <artifactId>nifi-hashicorp-vault-parameter-value-provider</artifactId>
+
+    <artifactId>nifi-hashicorp-vault-client-service</artifactId>
     <packaging>jar</packaging>
+
     <dependencies>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-stateless-api</artifactId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
             <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-vault-utils</artifactId>
+            <artifactId>nifi-hashicorp-vault</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ssl-context-service-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault-client-service-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
         <!-- test dependencies -->
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -41,9 +57,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-expression-language</artifactId>
+            <artifactId>nifi-security-utils</artifactId>
             <version>1.18.0-SNAPSHOT</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
+
 </project>
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
new file mode 100644
index 0000000000..f242d8658d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.apache.nifi.vault.hashicorp;
+
+import org.apache.nifi.annotation.behavior.DynamicProperties;
+import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.behavior.SupportsSensitiveDynamicProperties;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnDisabled;
+import org.apache.nifi.annotation.lifecycle.OnEnabled;
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.resource.ResourceReference;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultConfiguration;
+import org.springframework.core.env.PropertySource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+
+@Tags({"hashicorp", "vault", "client"})
+@CapabilityDescription("A controller service for interacting with HashiCorp 
Vault.")
+@SupportsSensitiveDynamicProperties
+@DynamicProperties(
+        @DynamicProperty(name = "A Spring Vault configuration property name",
+                value = "The property value",
+                description = "Allows any Spring Vault property keys to be 
specified, as described in " +
+                        
"(https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration).
 " +
+                        "See Additional Details for more information.",
+                expressionLanguageScope = 
ExpressionLanguageScope.VARIABLE_REGISTRY
+        )
+)
+public class StandardHashiCorpVaultClientService extends 
AbstractControllerService implements HashiCorpVaultClientService {
+
+    private static List<PropertyDescriptor> PROPERTIES = 
Collections.unmodifiableList(Arrays.asList(
+            CONFIGURATION_STRATEGY,
+            VAULT_URI,
+            VAULT_AUTHENTICATION,
+            SSL_CONTEXT_SERVICE,
+            VAULT_PROPERTIES_FILES,
+            CONNECTION_TIMEOUT,
+            READ_TIMEOUT
+    ));
+
+    private HashiCorpVaultCommunicationService communicationService;
+
+    @Override
+    protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final 
String propertyDescriptorName) {
+        return new PropertyDescriptor.Builder()
+                .name(propertyDescriptorName)
+                .displayName(propertyDescriptorName)
+                .dynamic(true)
+                
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+                .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+                .build();
+    }
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTIES;
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final ConfigurationContext 
context, final ComponentLog verificationLogger,
+                                                 final Map<String, String> 
variables) {
+        final List<ConfigVerificationResult> results = new ArrayList<>();
+        HashiCorpVaultCommunicationService service = null;
+        try {
+            service = createCommunicationService(context);
+            results.add(new ConfigVerificationResult.Builder()
+                    .outcome(ConfigVerificationResult.Outcome.SUCCESSFUL)
+                    .verificationStepName("Configure HashiCorp Vault Client")
+                    .explanation("Successfully configured HashiCorp Vault 
Client")
+                    .build());
+        } catch (final Exception e) {
+            verificationLogger.error("Failed to configure HashiCorp Vault 
Client", e);
+
+            results.add(new ConfigVerificationResult.Builder()
+                    .outcome(ConfigVerificationResult.Outcome.FAILED)
+                    .verificationStepName("Configure HashiCorp Vault Client")
+                    .explanation("Failed to configure HashiCorp Vault Client: 
" + e.getMessage())
+                    .build());
+        }
+        if (service != null) {
+            try {
+                service.getServerVersion();
+                results.add(new ConfigVerificationResult.Builder()
+                        .outcome(ConfigVerificationResult.Outcome.SUCCESSFUL)
+                        .verificationStepName("Connect to HashiCorp Vault 
Server")
+                        .explanation("Successfully connected to HashiCorp 
Vault Server")
+                        .build());
+            } catch (final Exception e) {
+                verificationLogger.error("Failed to connect to HashiCorp Vault 
Server", e);
+
+                results.add(new ConfigVerificationResult.Builder()
+                        .outcome(ConfigVerificationResult.Outcome.FAILED)
+                        .verificationStepName("Connect to HashiCorp Vault 
Server")
+                        .explanation("Failed to connect to HashiCorp Vault 
Server: " + e.getMessage())
+                        .build());
+            }
+        }
+
+        return results;
+    }
+
+    @OnEnabled
+    public void onEnabled(final ConfigurationContext context) throws 
InitializationException {
+        try {
+            communicationService = createCommunicationService(context);
+        } catch (final Exception e) {
+            throw new InitializationException("Failed to initialize HashiCorp 
Vault client", e);
+        }
+    }
+
+    @OnDisabled
+    public void onDisabled() {
+        communicationService = null;
+    }
+
+    @Override
+    public HashiCorpVaultCommunicationService 
getHashiCorpVaultCommunicationService() {
+        return communicationService;
+    }
+
+    private HashiCorpVaultCommunicationService 
createCommunicationService(final ConfigurationContext context) throws 
IOException {
+        final List<PropertySource<?>> propertySources = new ArrayList<>();
+
+        final String configurationStrategy = 
context.getProperty(CONFIGURATION_STRATEGY).getValue();
+        if (DIRECT_PROPERTIES.getValue().equals(configurationStrategy)) {
+            final PropertySource<?> configurationPropertySource = new 
DirectPropertySource("Direct Properties", context);
+            propertySources.add(configurationPropertySource);
+        } else {
+            for (final ResourceReference resourceReference : 
context.getProperty(VAULT_PROPERTIES_FILES).asResources().asList()) {
+                final String propertiesFile = resourceReference.getLocation();
+                
propertySources.add(HashiCorpVaultConfiguration.createPropertiesFileSource(propertiesFile));
+            }
+        }
+
+        return new 
StandardHashiCorpVaultCommunicationService(propertySources.toArray(new 
PropertySource[0]));
+    }
+
+    static class DirectPropertySource extends 
PropertySource<ConfigurationContext> {
+
+        private static final String VAULT_SSL_KEY_PATTERN = 
"vault.ssl.(key.*|trust.*|enabledProtocols)";
+
+        public DirectPropertySource(final String name, final 
ConfigurationContext source) {
+            super(name, source);
+        }
+
+        @Override
+        public Object getProperty(final String name) {
+            if (name.matches(VAULT_SSL_KEY_PATTERN)) {
+                return getSslProperty(name);
+            }
+
+            return getSource().getAllProperties().get(name);
+        }
+
+        private String getSslProperty(final String name) {
+            if (getSource().getProperty(SSL_CONTEXT_SERVICE).isSet()) {
+                final SSLContextService sslContextService = 
getSource().getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class);
+                switch (name) {
+                    case "vault.ssl.key-store":
+                        return sslContextService.getKeyStoreFile();
+                    case "vault.ssl.key-store-password":
+                        return sslContextService.getKeyStorePassword();
+                    case "vault.ssl.key-store-type":
+                        return sslContextService.getKeyStoreType();
+                    case "vault.ssl.trust-store":
+                        return sslContextService.getTrustStoreFile();
+                    case "vault.ssl.trust-store-password":
+                        return sslContextService.getTrustStorePassword();
+                    case "vault.ssl.trust-store-type":
+                        return sslContextService.getTrustStoreType();
+                    case "vault.ssl.enabledProtocols":
+                        return sslContextService.getSslAlgorithm();
+                }
+            }
+
+            return null;
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
new file mode 100644
index 0000000000..d20ace77f8
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
@@ -0,0 +1,15 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License 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.
+org.apache.nifi.vault.hashicorp.StandardHashiCorpVaultClientService
\ No newline at end of file
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/resources/docs/org.apache.nifi.vault.hashicorp.StandardHashiCorpVaultClientService/additionalDetails.html
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/resources/docs/org.apache.nifi.vault.hashicorp.StandardHashiCorpVaultClientService/additionalDetails.html
new file mode 100644
index 0000000000..337ee65164
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/resources/docs/org.apache.nifi.vault.hashicorp.StandardHashiCorpVaultClientService/additionalDetails.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html lang="en" xmlns="http://www.w3.org/1999/html";>
+<!--
+      Licensed to the Apache Software Foundation (ASF) under one or more
+      contributor license agreements.  See the NOTICE file distributed with
+      this work for additional information regarding copyright ownership.
+      The ASF licenses this file to You under the Apache License, Version 2.0
+      (the "License"); you may not use this file except in compliance with
+      the License.  You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License 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.
+    -->
+
+<head>
+    <meta charset="utf-8"/>
+    <title>StandardHashiCorpVaultClientService</title>
+    <link rel="stylesheet" href="../../../../../css/component-usage.css" 
type="text/css"/>
+</head>
+<body>
+
+<h1>Configuring the Bootstrap HashiCorp Vault Configuration File</h1>
+
+<p>
+    The ./conf/bootstrap-hashicorp-vault.conf file that comes with Apache NiFi 
is a convenient way to configure this
+    controller service in a manner consistent with the HashiCorpVault 
sensitive properties provider.  Since this file is already used for configuring
+    the Vault client for protecting sensitive properties in the NiFi 
configuration files
+    (see the <a 
href="../../../../../html/administration-guide.html#hashicorp-vault-providers">Administrator's
 Guide</a>),
+    it's a natural starting point for configuring the controller service as 
well.
+</p>
+
+<p>
+    An example configuration of this properties file is as follows:
+</p>
+
+<code>
+    <pre>
+# HTTP or HTTPS URI for HashiCorp Vault is required to enable the Sensitive 
Properties Provider
+vault.uri=https://127.0.0.1:8200
+
+# Optional file supports authentication properties described in the Spring 
Vault Environment Configuration
+# 
https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration
+#
+# All authentication properties must be included in 
bootstrap-hashicorp-vault.conf when this property is not specified.
+# Properties in bootstrap-hashicorp-vault.conf take precedence when the same 
values are defined in both files.
+# Token Authentication is the default when the 'vault.authentication' property 
is not specified.
+vault.authentication.properties.file=[full/path/to/vault-auth.properties]
+
+# Optional Timeout properties
+vault.connection.timeout=5 secs
+vault.read.timeout=15 secs
+
+# Optional TLS properties
+vault.ssl.enabledCipherSuites=
+vault.ssl.enabledProtocols=TLSv1.3
+vault.ssl.key-store=[path/to/keystore.p12]
+vault.ssl.key-store-type=PKCS12
+vault.ssl.key-store-password=[keystore password]
+vault.ssl.trust-store=[path/to/truststore.p12]
+vault.ssl.trust-store-type=PKCS12
+vault.ssl.trust-store-password=[truststore password]
+    </pre>
+</code>
+
+<p>
+    In order to use this file in the StandardHashiCorpVaultClientService, 
specify the following properties:
+    <ul>
+        <li><b>Configuration Strategy</b> - Properties Files</li>
+        <li><b>Vault Properties Files</b> - 
./conf/bootstrap-hashicorp-vault.conf</li>
+    </ul>
+</p>
+
+<p>
+    If your bootstrap configuration includes the 
vault.authentication.properties.file containing additional authentication 
properties, this
+    file will also need to be added to the Vault Properties Files property as 
a comma-separated value.
+</p>
+
+<h3>Configuring the Client using Direct Properties</h3>
+
+<p>
+    However, if you want to specify or override properties directly in the 
controller service, you may do this by specifying a Configuration Strategy
+    of 'Direct Properties'.  This can be useful if you are reusing an 
SSLContextService or want to parameterize the Vault configuration properties.
+    Authentication-related properties can also be added as sensitive dynamic 
properties, as seen in the examples below.
+</p>
+
+<h3>Vault Authentication</h3>
+<p>
+    Under the hood, the controller service uses Spring Vault, and directly 
supports the property keys specified in
+    <a 
href="https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration";>Spring
 Vault's documentation</a>.
+    Following are some common examples of authentication with Vault.
+</p>
+
+<h4>Token Authentication</h4>
+<p>
+    The simplest authentication scheme uses a rotating token, which is enabled 
by default in Vault.  To specify this mechanism, select "TOKEN" from the
+    "Vault Authentication" property (the default). However, since the token 
should rotate by nature, it is a best practice to use the 'Properties Files'
+    Configuration Strategy, and keep the token value in an external properties 
file, indicating this filename in the 'Vault Properties Files' property.
+    Then an external process can rotate the token in the file without updating 
NiFi configuration.  In order to pick up the changed token, the controller
+    service must be disabled and re-enabled.
+</p>
+
+<p>
+    For testing purposes, however, it may be more convenient to specify the 
token directly in the controller service.  To do so, add a new Sensitive 
property named
+    'vault.token' and enter the token as the value.
+</p>
+
+<h4>Certificate Authentication</h4>
+<p>
+    Certificate authentication must be enabled in the Vault server before it 
can be used from NiFi, but it uses the same TLS settings as the actual
+    client connection, so no additional authentication properties are 
required.  While these TLS settings can be provided in an external properties 
file,
+    we will demonstrate configuring an SSLContextService instead.
+</p>
+
+<p>
+    First, create an SSLContextService controller service and configure the 
Filename, Password, and Type for both the Keystore and Truststore.
+    Enable it, and assign it as the SSL Context Service in the Vault 
controller service.  Then, simply specify "CERT" as the "Vault Authentication"
+    property value.
+</p>
+
+<h4>Other Authentication Methods</h4>
+<p>
+    To configure the other authentication methods, see the Spring Vault 
documentation linked above.  All relevant properties should be added either
+    to the external properties files referenced in the "Vault Properties 
Files" property if using the 'Properties Files' Configuration Strategy,
+    or added as custom properties with the same name if using the 'Direct 
Properties' Configuration Strategy.
+    For example, for the Azure authentication mechanism, properties will have 
to be added for 'vault.azure-msi.azure-path',
+    'vault.azure-msi.role', and 'vault.azure-msi.identity-token-service'.
+</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultClientService.java
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultClientService.java
new file mode 100644
index 0000000000..cbcd583b76
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultClientService.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.apache.nifi.vault.hashicorp;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.components.resource.ResourceReference;
+import org.apache.nifi.components.resource.ResourceReferences;
+import org.apache.nifi.components.resource.ResourceType;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.security.util.KeyStoreUtils;
+import org.apache.nifi.security.util.StandardTlsConfiguration;
+import org.apache.nifi.security.util.TlsConfiguration;
+import org.apache.nifi.ssl.SSLContextService;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TestStandardHashiCorpVaultClientService {
+
+    private static Path bootstrapHashiCorpVaultConf;
+    private static TlsConfiguration tlsConfiguration;
+
+    private StandardHashiCorpVaultClientService clientService;
+
+    private ConfigurationContext mockContext(final Map<PropertyDescriptor, 
String> properties, final boolean includeSSL) {
+        final ConfigurationContext context = mock(ConfigurationContext.class);
+        properties.entrySet().forEach(entry -> mockProperty(context, 
entry.getKey(), entry.getValue()));
+        if 
(properties.containsKey(HashiCorpVaultClientService.VAULT_PROPERTIES_FILES)) {
+            final PropertyValue propertiesFilesProperty = 
mock(PropertyValue.class);
+            final ResourceReferences resources = 
mock(ResourceReferences.class);
+            
when(resources.asList()).thenReturn(Arrays.asList(properties.get(HashiCorpVaultClientService.VAULT_PROPERTIES_FILES).split(","))
+                    .stream().map(SimpleResourceReference::new)
+                    .collect(Collectors.toList()));
+            when(propertiesFilesProperty.asResources()).thenReturn(resources);
+            
when(context.getProperty(HashiCorpVaultClientService.VAULT_PROPERTIES_FILES)).thenReturn(propertiesFilesProperty);
+        }
+        if (includeSSL) {
+            final SSLContextService sslContextService = 
mock(SSLContextService.class);
+            
when(sslContextService.getKeyStoreFile()).thenReturn(tlsConfiguration.getKeystorePath());
+            
when(sslContextService.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType());
+            
when(sslContextService.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword());
+            
when(sslContextService.getTrustStoreFile()).thenReturn(tlsConfiguration.getTruststorePath());
+            
when(sslContextService.getTrustStoreType()).thenReturn(tlsConfiguration.getTruststoreType().getType());
+            
when(sslContextService.getTrustStorePassword()).thenReturn(tlsConfiguration.getTruststorePassword());
+            
when(sslContextService.getSslAlgorithm()).thenReturn(tlsConfiguration.getProtocol());
+
+            final PropertyValue sslContextServicePropertyValue = 
mock(PropertyValue.class);
+            when(sslContextServicePropertyValue.isSet()).thenReturn(true);
+            
when(sslContextServicePropertyValue.asControllerService(SSLContextService.class)).thenReturn(sslContextService);
+            
when(context.getProperty(HashiCorpVaultClientService.SSL_CONTEXT_SERVICE)).thenReturn(sslContextServicePropertyValue);
+        } else {
+            final PropertyValue sslContextServicePropertyValue = 
mock(PropertyValue.class);
+            when(sslContextServicePropertyValue.isSet()).thenReturn(false);
+            
when(context.getProperty(HashiCorpVaultClientService.SSL_CONTEXT_SERVICE)).thenReturn(sslContextServicePropertyValue);
+        }
+        
when(context.getAllProperties()).thenReturn(properties.entrySet().stream()
+                .collect(Collectors.toMap(e -> e.getKey().getName(), 
Map.Entry::getValue)));
+        return context;
+    }
+
+    private void mockProperty(final ConfigurationContext context, final 
PropertyDescriptor descriptor, final String value) {
+        final PropertyValue propertyValue = mock(PropertyValue.class);
+        when(propertyValue.getValue()).thenReturn(value);
+        when(context.getProperty(descriptor)).thenReturn(propertyValue);
+    }
+
+    @BeforeAll
+    public static void initOnce() throws IOException, GeneralSecurityException 
{
+        bootstrapHashiCorpVaultConf = 
Files.createTempFile("bootstrap-hashicorp-vault", "conf");
+        final File keyStoreFile = 
File.createTempFile(TestStandardHashiCorpVaultClientService.class.getSimpleName(),
 ".keystore.p12");
+        final File trustStoreFile = 
File.createTempFile(TestStandardHashiCorpVaultClientService.class.getSimpleName(),
 ".truststore.p12");
+        final TlsConfiguration requestedTlsConfig = new 
StandardTlsConfiguration();
+        tlsConfiguration = 
KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore(requestedTlsConfig, 7, 
null);
+        // This should be overridden by any explicit properties
+        Files.write(bootstrapHashiCorpVaultConf, 
(String.format("vault.uri=https://localhost:8200\n"; +
+                "vault.authentication=TOKEN\n" +
+                "vault.token=my-token\n" +
+                "vault.ssl.key-store=%s\n" +
+                "vault.ssl.key-store-password=%s\n" +
+                "vault.ssl.key-store-type=%s\n" +
+                "vault.ssl.trust-store=%s\n" +
+                "vault.ssl.trust-store-password=%s\n" +
+                "vault.ssl.trust-store-type=%s\n",
+                tlsConfiguration.getKeystorePath().replace("\\", "\\\\"),
+                tlsConfiguration.getKeystorePassword(),
+                tlsConfiguration.getKeystoreType().getType(),
+                tlsConfiguration.getTruststorePath().replace("\\", "\\\\"),
+                tlsConfiguration.getTruststorePassword(),
+                
tlsConfiguration.getTruststoreType().getType())).getBytes(StandardCharsets.UTF_8));
+    }
+
+    @AfterAll
+    public static void tearDownOnce() throws IOException {
+        Files.deleteIfExists(bootstrapHashiCorpVaultConf);
+        Files.deleteIfExists(Paths.get(tlsConfiguration.getKeystorePath()));
+        Files.deleteIfExists(Paths.get(tlsConfiguration.getTruststorePath()));
+    }
+
+    @BeforeEach
+    public void init() {
+        clientService = new StandardHashiCorpVaultClientService() {
+            @Override
+            protected ComponentLog getLogger() {
+                return mock(ComponentLog.class);
+            }
+        };
+    }
+
+    @Test
+    public void onEnabledHttpDirect() throws InitializationException {
+        final Map<PropertyDescriptor, String> properties = new HashMap<>();
+        final PropertyDescriptor vaultToken = new 
PropertyDescriptor.Builder().name("vault.token").build();
+        properties.put(HashiCorpVaultClientService.CONFIGURATION_STRATEGY, 
HashiCorpVaultClientService.DIRECT_PROPERTIES.getValue());
+        properties.put(HashiCorpVaultClientService.VAULT_URI, 
"http://localhost:8200";);
+        properties.put(vaultToken, "myToken");
+        properties.put(HashiCorpVaultClientService.VAULT_AUTHENTICATION, 
"TOKEN");
+        final ConfigurationContext context = mockContext(properties, false);
+        clientService.onEnabled(context);
+        assertNotNull(clientService.getHashiCorpVaultCommunicationService());
+        clientService.onDisabled();
+        assertNull(clientService.getHashiCorpVaultCommunicationService());
+    }
+
+    @Test
+    public void onEnabledHttpsDirect() throws InitializationException {
+        final Map<PropertyDescriptor, String> properties = new HashMap<>();
+        final PropertyDescriptor vaultToken = new 
PropertyDescriptor.Builder().name("vault.token").build();
+        properties.put(HashiCorpVaultClientService.CONFIGURATION_STRATEGY, 
HashiCorpVaultClientService.DIRECT_PROPERTIES.getValue());
+        properties.put(HashiCorpVaultClientService.VAULT_URI, 
"https://localhost:8200";);
+        properties.put(vaultToken, "myToken");
+        properties.put(HashiCorpVaultClientService.VAULT_AUTHENTICATION, 
"TOKEN");
+        final ConfigurationContext context = mockContext(properties, true);
+        clientService.onEnabled(context);
+        assertNotNull(clientService.getHashiCorpVaultCommunicationService());
+        clientService.onDisabled();
+        assertNull(clientService.getHashiCorpVaultCommunicationService());
+    }
+
+    @Test
+    public void onEnabledHttpsPropertiesFiles() throws InitializationException 
{
+        final Map<PropertyDescriptor, String> properties = new HashMap<>();
+        properties.put(HashiCorpVaultClientService.CONFIGURATION_STRATEGY, 
HashiCorpVaultClientService.PROPERTIES_FILES.getValue());
+        properties.put(HashiCorpVaultClientService.VAULT_PROPERTIES_FILES, 
bootstrapHashiCorpVaultConf.toString());
+        final ConfigurationContext context = mockContext(properties, true);
+        clientService.onEnabled(context);
+        assertNotNull(clientService.getHashiCorpVaultCommunicationService());
+        clientService.onDisabled();
+        assertNull(clientService.getHashiCorpVaultCommunicationService());
+    }
+
+    private class SimpleResourceReference implements ResourceReference {
+        private final String filename;
+
+        private SimpleResourceReference(String filename) {
+            this.filename = filename;
+        }
+
+        @Override
+        public File asFile() {
+            return null;
+        }
+
+        @Override
+        public URL asURL() {
+            return null;
+        }
+
+        @Override
+        public InputStream read() throws IOException {
+            return null;
+        }
+
+        @Override
+        public boolean isAccessible() {
+            return false;
+        }
+
+        @Override
+        public String getLocation() {
+            return filename;
+        }
+
+        @Override
+        public ResourceType getResourceType() {
+            return null;
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml
index 511f1a008a..fd6924cc74 100644
--- 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-nar/pom.xml
@@ -35,5 +35,27 @@
             
<artifactId>nifi-hashicorp-vault-parameter-value-provider</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            
<artifactId>nifi-hashicorp-vault-client-service-api-nar</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <type>nar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault-parameter-provider</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault-client-service</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/pom.xml
similarity index 85%
copy from 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
copy to 
nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/pom.xml
index b9cee8e038..80d227a12a 100755
--- 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/pom.xml
@@ -15,22 +15,32 @@
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <modelVersion>4.0.0</modelVersion>
+
     <parent>
         <groupId>org.apache.nifi</groupId>
         <artifactId>nifi-hashicorp-vault-bundle</artifactId>
         <version>1.18.0-SNAPSHOT</version>
     </parent>
-    <artifactId>nifi-hashicorp-vault-parameter-value-provider</artifactId>
+
+    <artifactId>nifi-hashicorp-vault-parameter-provider</artifactId>
     <packaging>jar</packaging>
+
     <dependencies>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-stateless-api</artifactId>
+            <artifactId>nifi-hashicorp-vault-client-service-api</artifactId>
             <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-vault-utils</artifactId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
         <!-- test dependencies -->
@@ -39,11 +49,6 @@
             <artifactId>jcl-over-slf4j</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-expression-language</artifactId>
-            <version>1.18.0-SNAPSHOT</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
+
 </project>
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
new file mode 100644
index 0000000000..154d0c6133
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.apache.nifi.vault.hashicorp;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.parameter.AbstractParameterProvider;
+import org.apache.nifi.parameter.Parameter;
+import org.apache.nifi.parameter.ParameterDescriptor;
+import org.apache.nifi.parameter.ParameterGroup;
+import org.apache.nifi.parameter.ParameterProvider;
+import org.apache.nifi.parameter.VerifiableParameterProvider;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@CapabilityDescription("Provides parameters from HashiCorp Vault Key/Value 
Secrets.  Each Secret represents a parameter group, " +
+        "which will map to a Parameter Context.  The keys and values in the 
Secret map to Parameters.")
+@Tags({"hashicorp", "vault", "secret"})
+public class HashiCorpVaultParameterProvider extends AbstractParameterProvider 
implements ParameterProvider, VerifiableParameterProvider {
+
+    public static final PropertyDescriptor VAULT_CLIENT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("vault-client-service")
+            .displayName("HashiCorp Vault Client Service")
+            .description("The service used to interact with HashiCorp Vault")
+            .identifiesControllerService(HashiCorpVaultClientService.class)
+            .addValidator(Validator.VALID)
+            .required(true)
+            .build();
+    public static final PropertyDescriptor KV_PATH = new 
PropertyDescriptor.Builder()
+            .name("kv-path")
+            .displayName("Key/Value Path")
+            .description("The HashiCorp Vault path to the Key/Value Secrets 
Engine")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .required(true)
+            .defaultValue("kv")
+            .build();
+    public static final PropertyDescriptor SECRET_NAME_PATTERN = new 
PropertyDescriptor.Builder()
+            .name("secret-name-pattern")
+            .displayName("Secret Name Pattern")
+            .description("A Regular Expression indicating which Secrets to 
include as parameter groups to map to Parameter Contexts by name.")
+            .addValidator(StandardValidators.REGULAR_EXPRESSION_VALIDATOR)
+            .required(true)
+            .defaultValue(".*")
+            .build();
+
+    private static final List<PropertyDescriptor> PROPERTIES = 
Collections.unmodifiableList(Arrays.asList(
+            VAULT_CLIENT_SERVICE,
+            KV_PATH,
+            SECRET_NAME_PATTERN));
+
+    private HashiCorpVaultCommunicationService vaultCommunicationService;
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTIES;
+    }
+
+    @Override
+    public List<ParameterGroup> fetchParameters(final ConfigurationContext 
context) {
+        if (vaultCommunicationService == null) {
+            vaultCommunicationService = getVaultCommunicationService(context);
+        }
+
+        final List<ParameterGroup> parameterGroups = 
getParameterGroups(vaultCommunicationService, context);
+        return parameterGroups;
+    }
+
+    private List<ParameterGroup> getParameterGroups(final 
HashiCorpVaultCommunicationService vaultCommunicationService,
+                                                            final 
ConfigurationContext context) {
+        final String kvPath = context.getProperty(KV_PATH).getValue();
+        final String secretIncludeRegex = 
context.getProperty(SECRET_NAME_PATTERN).getValue();
+        final List<String> allSecretNames = 
vaultCommunicationService.listKeyValueSecrets(kvPath);
+        final List<String> secretNames = allSecretNames.stream()
+                .filter(name -> name.matches(secretIncludeRegex))
+                .collect(Collectors.toList());
+
+        final List<ParameterGroup> parameterGroups = new ArrayList<>();
+        for (final String secretName : secretNames) {
+            final Map<String, String> keyValues = 
vaultCommunicationService.readKeyValueSecretMap(kvPath, secretName);
+            final List<Parameter> parameters = new ArrayList<>();
+            keyValues.forEach( (key, value) -> {
+                final ParameterDescriptor parameterDescriptor = new 
ParameterDescriptor.Builder().name(key).build();
+                parameters.add(new Parameter(parameterDescriptor, value, null, 
true));
+            });
+            parameterGroups.add(new ParameterGroup(secretName, parameters));
+        }
+        final long parameterCount = parameterGroups.stream()
+                .flatMap(group -> group.getParameters().stream())
+                .count();
+        final List<String> parameterGroupNames = parameterGroups.stream()
+                .map(group -> group.getGroupName())
+                .distinct()
+                .collect(Collectors.toList());
+        getLogger().info("Fetched parameter groups {}, containing a total of 
{} parameters", parameterGroupNames, parameterCount);
+        return parameterGroups;
+    }
+
+    @Override
+    public void onPropertyModified(final PropertyDescriptor descriptor, final 
String oldValue, final String newValue) {
+        if (VAULT_CLIENT_SERVICE.equals(descriptor)) {
+            vaultCommunicationService = null;
+        }
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final ConfigurationContext 
context, final ComponentLog verificationLogger) {
+        final List<ConfigVerificationResult> results = new ArrayList<>();
+        try {
+            final HashiCorpVaultCommunicationService vaultCommunicationService 
= getVaultCommunicationService(context);
+            final List<ParameterGroup> parameterGroups = 
getParameterGroups(vaultCommunicationService, context);
+            final int groupCount = parameterGroups.size();
+            final long parameterCount = parameterGroups.stream()
+                    .flatMap(group -> group.getParameters().stream())
+                    .count();
+            results.add(new ConfigVerificationResult.Builder()
+                    .outcome(ConfigVerificationResult.Outcome.SUCCESSFUL)
+                    .verificationStepName("Fetch Secrets as Parameter Groups")
+                    .explanation(String.format("Successfully fetched %s 
secrets matching the filter as Parameter Groups, " +
+                            "containing a total of %s Parameters.", 
groupCount, parameterCount))
+                    .build());
+        } catch (final Exception e) {
+            verificationLogger.error("Failed to fetch secrets as Parameter 
Groups", e);
+            results.add(new ConfigVerificationResult.Builder()
+                    .outcome(ConfigVerificationResult.Outcome.FAILED)
+                    .verificationStepName("Fetch Secrets as Parameter Groups")
+                    .explanation(String.format("Failed to fetch secrets as 
Parameter Groups: " + e.getMessage()))
+                    .build());
+        }
+
+        return results;
+    }
+
+    HashiCorpVaultCommunicationService getVaultCommunicationService(final 
ConfigurationContext context) {
+        final HashiCorpVaultClientService clientService = 
context.getProperty(VAULT_CLIENT_SERVICE)
+                .asControllerService(HashiCorpVaultClientService.class);
+        return clientService.getHashiCorpVaultCommunicationService();
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/resources/META-INF/services/org.apache.nifi.parameter.ParameterProvider
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/resources/META-INF/services/org.apache.nifi.parameter.ParameterProvider
new file mode 100644
index 0000000000..34f9c97d31
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/resources/META-INF/services/org.apache.nifi.parameter.ParameterProvider
@@ -0,0 +1,15 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License 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.
+org.apache.nifi.vault.hashicorp.HashiCorpVaultParameterProvider
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
new file mode 100644
index 0000000000..9880448c67
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.apache.nifi.vault.hashicorp;
+
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.parameter.Parameter;
+import org.apache.nifi.parameter.ParameterDescriptor;
+import org.apache.nifi.parameter.ParameterGroup;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class TestHashiCorpVaultParameterProvider {
+
+    private HashiCorpVaultParameterProvider parameterProvider;
+
+    @Mock
+    private HashiCorpVaultCommunicationService vaultCommunicationService;
+
+    private List<ParameterGroup> mockedGroups;
+
+    @BeforeEach
+    public void init() {
+        vaultCommunicationService = 
mock(HashiCorpVaultCommunicationService.class);
+
+        parameterProvider = new HashiCorpVaultParameterProvider() {
+            @Override
+            HashiCorpVaultCommunicationService 
getVaultCommunicationService(final ConfigurationContext context) {
+                return vaultCommunicationService;
+            }
+
+            @Override
+            protected ComponentLog getLogger() {
+                return mock(ComponentLog.class);
+            }
+        };
+        mockedGroups = new ArrayList<>();
+        mockedGroups.add(new ParameterGroup("groupA", Arrays.asList(
+                createParameter("paramA", "valueA"),
+                createParameter("paramB", "valueB"),
+                createParameter("otherParam", "valueC"))));
+        mockedGroups.add(new ParameterGroup("groupA", Arrays.asList(
+                createParameter("paramC", "valueC"),
+                createParameter("paramD", "valueD"),
+                createParameter("otherParam2", "valueE"))));
+        mockedGroups.add(new ParameterGroup("groupB", Arrays.asList(
+                createParameter("paramC", "valueC"),
+                createParameter("paramD", "valueD"),
+                createParameter("otherParam", "valueE"))));
+    }
+
+    @Test
+    public void testFetchParameters() {
+        mockSecrets("kv2", mockedGroups);
+
+        final Map<PropertyDescriptor, String> properties = new HashMap<>();
+        properties.put(HashiCorpVaultParameterProvider.KV_PATH, "kv2");
+        properties.put(HashiCorpVaultParameterProvider.VAULT_CLIENT_SERVICE, 
"service");
+        properties.put(HashiCorpVaultParameterProvider.SECRET_NAME_PATTERN, 
".*");
+        final ConfigurationContext context = mockContext(properties);
+
+        final List<ParameterGroup> results = 
parameterProvider.fetchParameters(context);
+        assertEquals(3, results.size());
+        results.forEach(group -> {
+            assertEquals(3, group.getParameters().size());
+        });
+    }
+
+    @Test
+    public void testFetchParametersSecretRegex() {
+        mockSecrets("kv2", mockedGroups);
+
+        final Map<PropertyDescriptor, String> properties = new HashMap<>();
+        properties.put(HashiCorpVaultParameterProvider.KV_PATH, "kv2");
+        properties.put(HashiCorpVaultParameterProvider.VAULT_CLIENT_SERVICE, 
"service");
+        properties.put(HashiCorpVaultParameterProvider.SECRET_NAME_PATTERN, 
".*A");
+        final ConfigurationContext context = mockContext(properties);
+
+        final List<ParameterGroup> results = 
parameterProvider.fetchParameters(context);
+        assertEquals(2, results.size());
+        results.forEach(group -> {
+            assertEquals(3, group.getParameters().size());
+        });
+    }
+
+    @Test
+    public void testVerifyParameters() {
+        mockSecrets("kv2", mockedGroups);
+
+        final Map<PropertyDescriptor, String> properties = new HashMap<>();
+        properties.put(HashiCorpVaultParameterProvider.KV_PATH, "kv2");
+        properties.put(HashiCorpVaultParameterProvider.VAULT_CLIENT_SERVICE, 
"service");
+        properties.put(HashiCorpVaultParameterProvider.SECRET_NAME_PATTERN, 
".*");
+        final ConfigurationContext context = mockContext(properties);
+
+        final List<ConfigVerificationResult> results = 
parameterProvider.verify(context, mock(ComponentLog.class));
+        assertEquals(1, results.size());
+        final String explanation = results.get(0).getExplanation();
+        assertTrue(explanation.contains("3 secrets"));
+        assertTrue(explanation.contains("9 Parameters"));
+    }
+
+    private ConfigurationContext mockContext(final Map<PropertyDescriptor, 
String> properties) {
+        final ConfigurationContext context = mock(ConfigurationContext.class);
+        properties.entrySet().forEach(entry -> mockProperty(context, 
entry.getKey(), entry.getValue()));
+        return context;
+    }
+
+    private void mockProperty(final ConfigurationContext context, final 
PropertyDescriptor descriptor, final String value) {
+        final PropertyValue propertyValue = mock(PropertyValue.class);
+        lenient().when(propertyValue.getValue()).thenReturn(value);
+        
lenient().when(context.getProperty(descriptor)).thenReturn(propertyValue);
+    }
+
+    private void mockSecrets(final String kvPath, final List<ParameterGroup> 
parameterGroups) {
+        when(vaultCommunicationService.listKeyValueSecrets(kvPath))
+                .thenReturn(parameterGroups.stream().map(group -> 
group.getGroupName()).collect(Collectors.toList()));
+        for (final ParameterGroup parameterGroup : parameterGroups) {
+            final Map<String, String> keyValues = 
parameterGroup.getParameters().stream()
+                    .collect(Collectors.toMap(parameter -> 
parameter.getDescriptor().getName(), parameter -> parameter.getValue()));
+            
lenient().when(vaultCommunicationService.readKeyValueSecretMap(kvPath, 
parameterGroup.getGroupName())).thenReturn(keyValues);
+        }
+    }
+
+    private Parameter createParameter(final String name, final String value) {
+        return new Parameter(new 
ParameterDescriptor.Builder().name(name).build(), value);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
index b9cee8e038..2a686186fd 100755
--- 
a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
+++ 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-value-provider/pom.xml
@@ -30,7 +30,13 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-vault-utils</artifactId>
+            <artifactId>nifi-hashicorp-vault-api</artifactId>
+            <version>1.18.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hashicorp-vault</artifactId>
             <version>1.18.0-SNAPSHOT</version>
         </dependency>
         <!-- test dependencies -->
diff --git a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/pom.xml 
b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/pom.xml
index 14c80f5080..12737a7461 100644
--- a/nifi-nar-bundles/nifi-hashicorp-vault-bundle/pom.xml
+++ b/nifi-nar-bundles/nifi-hashicorp-vault-bundle/pom.xml
@@ -25,6 +25,10 @@
     <description>A bundle for reading and writing secrets from HashiCorp 
Vault</description>
     <modules>
         <module>nifi-hashicorp-vault-parameter-value-provider</module>
+        <module>nifi-hashicorp-vault-client-service-api</module>
+        <module>nifi-hashicorp-vault-client-service-api-nar</module>
+        <module>nifi-hashicorp-vault-client-service</module>
+        <module>nifi-hashicorp-vault-parameter-provider</module>
         <module>nifi-hashicorp-vault-nar</module>
     </modules>
 </project>

Reply via email to