You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2019/08/19 15:17:28 UTC

[nifi] branch master updated: NIFI-6364 - Add CLI commands for parameter contexts

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 16f3dbd  NIFI-6364 - Add CLI commands for parameter contexts
16f3dbd is described below

commit 16f3dbdc6a2ac2d1b666f060792a29c7fc9eec89
Author: Bryan Bende <bb...@apache.org>
AuthorDate: Mon Jun 17 15:24:30 2019 -0400

    NIFI-6364 - Add CLI commands for parameter contexts
    
    This closes #3628.
    
    Signed-off-by: Mark Payne <ma...@hotmail.com>
---
 .../toolkit/cli/impl/client/NiFiClientFactory.java |  16 +++
 .../toolkit/cli/impl/client/nifi/NiFiClient.java   |   8 ++
 .../cli/impl/client/nifi/ParamContextClient.java   |  41 ++++++
 .../impl/client/nifi/impl/JerseyNiFiClient.java    |  18 +++
 .../client/nifi/impl/JerseyParamContextClient.java | 157 +++++++++++++++++++++
 .../toolkit/cli/impl/command/AbstractCommand.java  |  37 +++++
 .../toolkit/cli/impl/command/CommandOption.java    |  13 ++
 .../toolkit/cli/impl/command/CommandProcessor.java |   1 +
 .../cli/impl/command/nifi/NiFiCommandGroup.java    |  22 +++
 .../params/AbstractUpdateParamContextCommand.java  |  84 +++++++++++
 .../command/nifi/params/CreateParamContext.java    |  78 ++++++++++
 .../cli/impl/command/nifi/params/DeleteParam.java  | 106 ++++++++++++++
 .../command/nifi/params/DeleteParamContext.java    |  63 +++++++++
 .../command/nifi/params/ExportParamContext.java    | 130 +++++++++++++++++
 .../impl/command/nifi/params/GetParamContext.java  |  57 ++++++++
 .../command/nifi/params/ImportParamContext.java    |  93 ++++++++++++
 .../command/nifi/params/ListParamContexts.java     |  50 +++++++
 .../command/nifi/params/MergeParamContext.java     | 136 ++++++++++++++++++
 .../cli/impl/command/nifi/params/SetParam.java     | 129 +++++++++++++++++
 .../impl/command/nifi/pg/PGGetParamContext.java    |  81 +++++++++++
 .../impl/command/nifi/pg/PGSetParamContext.java    |  79 +++++++++++
 .../command/registry/flow/ImportFlowVersion.java   |  17 +--
 .../cli/impl/result/nifi/ParamContextResult.java   |  82 +++++++++++
 .../cli/impl/result/nifi/ParamContextsResult.java  | 118 ++++++++++++++++
 24 files changed, 1600 insertions(+), 16 deletions(-)

diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiClientFactory.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiClientFactory.java
index 4e51c8a..07e4d41 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiClientFactory.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiClientFactory.java
@@ -25,6 +25,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerServicesClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.PoliciesClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ReportingTasksClient;
@@ -261,6 +262,21 @@ public class NiFiClientFactory implements ClientFactory<NiFiClient> {
         }
 
         @Override
+        public ParamContextClient getParamContextClient() {
+            return wrappedClient.getParamContextClientForProxiedEntities(proxiedEntity);
+        }
+
+        @Override
+        public ParamContextClient getParamContextClientForProxiedEntities(String... proxiedEntity) {
+            return wrappedClient.getParamContextClientForProxiedEntities(proxiedEntity);
+        }
+
+        @Override
+        public ParamContextClient getParamContextClientForToken(String token) {
+            return wrappedClient.getParamContextClientForToken(token);
+        }
+
+        @Override
         public void close() throws IOException {
             wrappedClient.close();
         }
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClient.java
index 6332fc7..dfe16f7 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClient.java
@@ -129,6 +129,14 @@ public interface NiFiClient extends Closeable {
 
     ReportingTasksClient getReportingTasksClientForToken(String token);
 
+    // ----- ParamContextClient -----
+
+    ParamContextClient getParamContextClient();
+
+    ParamContextClient getParamContextClientForProxiedEntities(String ... proxiedEntity);
+
+    ParamContextClient getParamContextClientForToken(String token);
+
     /**
      * The builder interface that implementations should provide for obtaining the client.
      */
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ParamContextClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ParamContextClient.java
new file mode 100644
index 0000000..790471d
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ParamContextClient.java
@@ -0,0 +1,41 @@
+/*
+ * 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.toolkit.cli.impl.client.nifi;
+
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
+import org.apache.nifi.web.api.entity.ParameterContextsEntity;
+
+import java.io.IOException;
+
+public interface ParamContextClient {
+
+    ParameterContextsEntity getParamContexts() throws NiFiClientException, IOException;
+
+    ParameterContextEntity getParamContext(String id) throws NiFiClientException, IOException;
+
+    ParameterContextEntity createParamContext(ParameterContextEntity paramContext) throws NiFiClientException, IOException;
+
+    ParameterContextEntity deleteParamContext(String id, String version) throws NiFiClientException, IOException;
+
+    ParameterContextUpdateRequestEntity updateParamContext(ParameterContextEntity paramContext) throws NiFiClientException, IOException;
+
+    ParameterContextUpdateRequestEntity getParamContextUpdateRequest(String contextId, String updateRequestId) throws NiFiClientException, IOException;
+
+    ParameterContextUpdateRequestEntity deleteParamContextUpdateRequest(String contextId, String updateRequestId) throws NiFiClientException, IOException;
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java
index 60ed5ba..c91067a 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java
@@ -27,6 +27,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerServicesClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.PoliciesClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.ReportingTasksClient;
@@ -268,6 +269,23 @@ public class JerseyNiFiClient implements NiFiClient {
     }
 
     @Override
+    public ParamContextClient getParamContextClient() {
+        return new JerseyParamContextClient(baseTarget);
+    }
+
+    @Override
+    public ParamContextClient getParamContextClientForProxiedEntities(String... proxiedEntity) {
+        final Map<String, String> headers = getHeaders(proxiedEntity);
+        return new JerseyParamContextClient(baseTarget, headers);
+    }
+
+    @Override
+    public ParamContextClient getParamContextClientForToken(String base64token) {
+        final Map<String, String> headers = getHeadersWithToken(base64token);
+        return new JerseyParamContextClient(baseTarget, headers);
+    }
+
+    @Override
     public void close() throws IOException {
         if (this.client != null) {
             try {
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyParamContextClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyParamContextClient.java
new file mode 100644
index 0000000..dbe8596
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyParamContextClient.java
@@ -0,0 +1,157 @@
+/*
+ * 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.toolkit.cli.impl.client.nifi.impl;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
+import org.apache.nifi.web.api.entity.ParameterContextsEntity;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+public class JerseyParamContextClient extends AbstractJerseyClient implements ParamContextClient {
+
+    private final WebTarget flowTarget;
+    private final WebTarget paramContextTarget;
+
+    public JerseyParamContextClient(final WebTarget baseTarget) {
+        this(baseTarget, Collections.emptyMap());
+    }
+
+    public JerseyParamContextClient(final WebTarget baseTarget, final Map<String,String> headers) {
+        super(headers);
+        this.flowTarget = baseTarget.path("/flow");
+        this.paramContextTarget = baseTarget.path("/parameter-contexts");
+    }
+
+    @Override
+    public ParameterContextsEntity getParamContexts() throws NiFiClientException, IOException {
+        return executeAction("Error retrieving parameter contexts", () -> {
+            final WebTarget target = flowTarget.path("/parameter-contexts");
+            return getRequestBuilder(target).get(ParameterContextsEntity.class);
+        });
+    }
+
+    @Override
+    public ParameterContextEntity getParamContext(final String id) throws NiFiClientException, IOException {
+        if (StringUtils.isBlank(id)) {
+            throw new IllegalArgumentException("Parameter context id cannot be null or blank");
+        }
+
+        return executeAction("Error retrieving parameter context", () -> {
+            final WebTarget target = paramContextTarget.path("{id}")
+                    .resolveTemplate("id", id);
+            return getRequestBuilder(target).get(ParameterContextEntity.class);
+        });
+    }
+
+    @Override
+    public ParameterContextEntity createParamContext(final ParameterContextEntity paramContext) throws NiFiClientException, IOException {
+        if (paramContext == null) {
+            throw new IllegalArgumentException("Parameter context cannot be null or blank");
+        }
+
+        return executeAction("Error creating parameter context", () -> {
+            final WebTarget target = paramContextTarget;
+            return getRequestBuilder(target).post(
+                    Entity.entity(paramContext, MediaType.APPLICATION_JSON),
+                    ParameterContextEntity.class
+            );
+        });
+    }
+
+    @Override
+    public ParameterContextEntity deleteParamContext(final String id, final String version) throws NiFiClientException, IOException {
+        if (StringUtils.isBlank(id)) {
+            throw new IllegalArgumentException("Parameter context id cannot be null or blank");
+        }
+
+        if (StringUtils.isBlank(version)) {
+            throw new IllegalArgumentException("Version cannot be null or blank");
+        }
+
+        return executeAction("Error deleting parameter context", () -> {
+            final WebTarget target = paramContextTarget.path("{id}")
+                    .resolveTemplate("id", id)
+                    .queryParam("version", version);
+            return getRequestBuilder(target).delete(ParameterContextEntity.class);
+        });
+    }
+
+    @Override
+    public ParameterContextUpdateRequestEntity updateParamContext(final ParameterContextEntity paramContext)
+            throws NiFiClientException, IOException {
+        if (paramContext == null) {
+            throw new IllegalArgumentException("Parameter context entity cannot be null");
+        }
+
+        if (paramContext.getComponent() == null) {
+            throw new IllegalArgumentException("Parameter context DTO cannot be null");
+        }
+
+        final String paramContextId = paramContext.getComponent().getId();
+        if (StringUtils.isBlank(paramContextId)) {
+            throw new IllegalArgumentException("Parameter context id cannot be null or blank");
+        }
+
+        return executeAction("Error creating parameter context update request", () -> {
+            final WebTarget target = paramContextTarget.path("{id}/update-requests")
+                    .resolveTemplate("id", paramContextId);
+            return getRequestBuilder(target).post(
+                    Entity.entity(paramContext, MediaType.APPLICATION_JSON),
+                    ParameterContextUpdateRequestEntity.class
+            );
+        });
+    }
+
+    @Override
+    public ParameterContextUpdateRequestEntity getParamContextUpdateRequest(final String contextId, final String updateRequestId)
+            throws NiFiClientException, IOException {
+        if (StringUtils.isBlank(updateRequestId)) {
+            throw new IllegalArgumentException("Parameter context update request id cannot be null or blank");
+        }
+
+        return executeAction("Error retrieving parameter context", () -> {
+            final WebTarget target = paramContextTarget.path("{context-id}/update-requests/{request-id}")
+                    .resolveTemplate("context-id", contextId)
+                    .resolveTemplate("request-id", updateRequestId);
+            return getRequestBuilder(target).get(ParameterContextUpdateRequestEntity.class);
+        });
+    }
+
+    @Override
+    public ParameterContextUpdateRequestEntity deleteParamContextUpdateRequest(final String contextId, final String updateRequestId)
+            throws NiFiClientException, IOException {
+        if (StringUtils.isBlank(updateRequestId)) {
+            throw new IllegalArgumentException("Parameter context update request id cannot be null or blank");
+        }
+
+        return executeAction("Error deleting parameter context update request", () -> {
+            final WebTarget target = paramContextTarget.path("{context-id}/update-requests/{request-id}")
+                    .resolveTemplate("context-id", contextId)
+                    .resolveTemplate("request-id", updateRequestId);
+            return getRequestBuilder(target).delete(ParameterContextUpdateRequestEntity.class);
+        });
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
index b692bb0..948f394 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
@@ -20,6 +20,7 @@ import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.MissingOptionException;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.Validate;
 import org.apache.nifi.toolkit.cli.api.Command;
@@ -27,8 +28,14 @@ import org.apache.nifi.toolkit.cli.api.Context;
 import org.apache.nifi.toolkit.cli.api.Result;
 import org.apache.nifi.toolkit.cli.api.ResultType;
 
+import java.io.IOException;
 import java.io.PrintStream;
 import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
 import java.util.Properties;
 
 /**
@@ -205,6 +212,10 @@ public abstract class AbstractCommand<R extends Result> implements Command<R> {
         }
     }
 
+    protected boolean hasArg(final Properties properties, final CommandOption option) {
+        return properties.containsKey(option.getLongName());
+    }
+
     protected boolean isVerbose(final Properties properties) {
         return properties.containsKey(CommandOption.VERBOSE.getLongName());
     }
@@ -213,4 +224,30 @@ public abstract class AbstractCommand<R extends Result> implements Command<R> {
         return getContext().isInteractive();
     }
 
+    protected void printIfInteractive(final String val) {
+        if (isInteractive()) {
+            print(val);
+        }
+    }
+
+    protected void printlnIfInteractive(final String val) {
+        if (isInteractive()) {
+            println(val);
+        }
+    }
+
+    protected String getInputSourceContent(String inputFile) throws IOException {
+        String contents;
+        try {
+            // try a public resource URL
+            URL url = new URL(inputFile);
+            contents = IOUtils.toString(url, StandardCharsets.UTF_8);
+        } catch (MalformedURLException e) {
+            // assume a local file then
+            URI uri = Paths.get(inputFile).toAbsolutePath().toUri();
+            contents = IOUtils.toString(uri, StandardCharsets.UTF_8);
+        }
+        return contents;
+    }
+
 }
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
index ab74cfe..538c198 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
@@ -102,6 +102,16 @@ public enum CommandOption {
     // NiFi - Templates
     TEMPLATE_ID("tid", "templateId", "The id of a template", true),
 
+    // NiFI - Parameter Contexts
+    PARAM_CONTEXT_ID("pcid", "paramContextId", "The id of a parameter context", true),
+    PARAM_CONTEXT_NAME("pcn", "paramContextName", "The name of a parameter context", true),
+    PARAM_CONTEXT_DESC("pcd", "paramContextDescription", "The description of a parameter context", true),
+
+    PARAM_NAME("pn", "paramName", "The name of the parameter", true),
+    PARAM_DESC("pd", "paramDescription", "The description of the parameter", true),
+    PARAM_VALUE("pv", "paramValue", "The value of a parameter", true),
+    PARAM_SENSITIVE("ps", "paramSensitive", "Whether or not the parameter is sensitive", true),
+
     // Security related
     KEYSTORE("ks", "keystore", "A keystore to use for TLS/SSL connections", true),
     KEYSTORE_TYPE("kst", "keystoreType", "The type of key store being used (JKS or PKCS12)", true),
@@ -160,4 +170,7 @@ public enum CommandOption {
         return Option.builder(shortName).longOpt(longName).desc(description).hasArg(hasArg).build();
     }
 
+    public Option createOption(final String description) {
+        return Option.builder(shortName).longOpt(longName).desc(description).hasArg(hasArg).build();
+    }
 }
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
index 256cb14..8b8d58f 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java
@@ -160,6 +160,7 @@ public class CommandProcessor {
             for (ResolvedReference resolvedRef : resolvedReferences) {
                 out.println();
                 out.printf("Using a positional back-reference for '%s'%n", resolvedRef.getDisplayName());
+                out.println();
             }
         }
     }
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
index 4361e6d..b41dae8 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
@@ -37,16 +37,26 @@ import org.apache.nifi.toolkit.cli.impl.command.nifi.nodes.DeleteNode;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.nodes.DisconnectNode;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.nodes.GetNode;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.nodes.GetNodes;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.ExportParamContext;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.ImportParamContext;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.SetParam;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.DeleteParam;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.DeleteParamContext;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.GetParamContext;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.ListParamContexts;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.MergeParamContext;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGChangeVersion;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGCreateControllerService;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGDisableControllerServices;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGEnableControllerServices;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetAllVersions;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetControllerServices;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetParamContext;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVars;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVersion;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGImport;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGList;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGSetParamContext;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGSetVar;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStart;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStatus;
@@ -65,6 +75,7 @@ import org.apache.nifi.toolkit.cli.impl.command.nifi.tenants.CreateUserGroup;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.tenants.ListUserGroups;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.tenants.ListUsers;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.tenants.UpdateUserGroup;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.params.CreateParamContext;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -110,6 +121,8 @@ public class NiFiCommandGroup extends AbstractCommandGroup {
         commands.add(new PGCreateControllerService());
         commands.add(new PGEnableControllerServices());
         commands.add(new PGDisableControllerServices());
+        commands.add(new PGGetParamContext());
+        commands.add(new PGSetParamContext());
         commands.add(new GetControllerServices());
         commands.add(new GetControllerService());
         commands.add(new CreateControllerService());
@@ -130,6 +143,15 @@ public class NiFiCommandGroup extends AbstractCommandGroup {
         commands.add(new ListTemplates());
         commands.add(new DownloadTemplate());
         commands.add(new UploadTemplate());
+        commands.add(new ListParamContexts());
+        commands.add(new GetParamContext());
+        commands.add(new CreateParamContext());
+        commands.add(new DeleteParamContext());
+        commands.add(new SetParam());
+        commands.add(new DeleteParam());
+        commands.add(new ExportParamContext());
+        commands.add(new ImportParamContext());
+        commands.add(new MergeParamContext());
         return new ArrayList<>(commands);
     }
 }
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/AbstractUpdateParamContextCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/AbstractUpdateParamContextCommand.java
new file mode 100644
index 0000000..1101377
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/AbstractUpdateParamContextCommand.java
@@ -0,0 +1,84 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.Result;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public abstract class AbstractUpdateParamContextCommand<R extends Result> extends AbstractNiFiCommand<R> {
+
+    public AbstractUpdateParamContextCommand(final String name, final Class<R> resultClass) {
+        super(name, resultClass);
+    }
+
+    protected ParameterContextUpdateRequestEntity performUpdate(final ParamContextClient client, final ParameterContextEntity parameterContextEntity,
+                                                                final ParameterContextUpdateRequestEntity updateRequestEntity)
+            throws NiFiClientException, IOException {
+
+        final AtomicBoolean cancelled = new AtomicBoolean(false);
+
+        // poll the update request for up to 30 seconds to see if it has completed
+        // if it doesn't complete then an exception will be thrown, but in either case the request will be deleted
+        final String contextId = parameterContextEntity.getId();
+        final String updateRequestId = updateRequestEntity.getRequest().getRequestId();
+        try {
+            boolean completed = false;
+            for (int i = 0; i < 30; i++) {
+                final ParameterContextUpdateRequestEntity retrievedUpdateRequest = client.getParamContextUpdateRequest(contextId, updateRequestId);
+                if (retrievedUpdateRequest != null && retrievedUpdateRequest.getRequest().isComplete()) {
+                    completed = true;
+                    break;
+                } else {
+                    try {
+                        if (getContext().isInteractive()) {
+                            println("Waiting for update request to complete...");
+                        }
+                        Thread.sleep(2000);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            if (!completed) {
+                cancelled.set(true);
+            }
+
+        } finally {
+            final ParameterContextUpdateRequestEntity deleteUpdateRequest = client.deleteParamContextUpdateRequest(contextId, updateRequestId);
+
+            final String failureReason = deleteUpdateRequest.getRequest().getFailureReason();
+            if (!StringUtils.isBlank(failureReason)) {
+                throw new NiFiClientException(failureReason);
+            }
+
+            if (cancelled.get()) {
+                throw new NiFiClientException("Unable to update parameter context, cancelling update request");
+            }
+
+            return deleteUpdateRequest;
+        }
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/CreateParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/CreateParamContext.java
new file mode 100644
index 0000000..b3575ca
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/CreateParamContext.java
@@ -0,0 +1,78 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Properties;
+
+public class CreateParamContext extends AbstractNiFiCommand<StringResult> {
+
+    public CreateParamContext() {
+        super("create-param-context", StringResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Creates a parameter context with the given name. " +
+                "After creating the parameter context, parameters can be added using the set-param command.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PARAM_CONTEXT_NAME.createOption());
+        addOption(CommandOption.PARAM_CONTEXT_DESC.createOption());
+    }
+
+    @Override
+    public StringResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        final String paramContextName = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_NAME);
+        final String paramContextDesc = getArg(properties, CommandOption.PARAM_CONTEXT_DESC);
+
+        final ParameterContextDTO paramContextDTO = new ParameterContextDTO();
+        paramContextDTO.setName(paramContextName);
+        paramContextDTO.setParameters(Collections.emptySet());
+
+        if (!StringUtils.isBlank(paramContextDesc)) {
+            paramContextDTO.setDescription(paramContextDesc);
+        }
+
+        final ParameterContextEntity paramContextEntity = new ParameterContextEntity();
+        paramContextEntity.setComponent(paramContextDTO);
+        paramContextEntity.setRevision(getInitialRevisionDTO());
+
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity createdParamContext = paramContextClient.createParamContext(paramContextEntity);
+        return new StringResult(createdParamContext.getId(), isInteractive());
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/DeleteParam.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/DeleteParam.java
new file mode 100644
index 0000000..edf3e1b
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/DeleteParam.java
@@ -0,0 +1,106 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.dto.ParameterDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
+import org.apache.nifi.web.api.entity.ParameterEntity;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Properties;
+
+public class DeleteParam extends AbstractUpdateParamContextCommand<VoidResult> {
+
+    public DeleteParam() {
+        super("delete-param", VoidResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Deletes a given parameter from the given parameter context.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+        addOption(CommandOption.PARAM_NAME.createOption());
+    }
+
+    @Override
+    public VoidResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        // Required args...
+        final String paramContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+        final String paramName = getRequiredArg(properties, CommandOption.PARAM_NAME);
+
+        // Ensure the context exists...
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity existingEntity = paramContextClient.getParamContext(paramContextId);
+
+        // Determine if this is an existing param or a new one...
+        final Optional<ParameterDTO> existingParam = existingEntity.getComponent().getParameters().stream()
+                .map(p -> p.getParameter())
+                .filter(p -> p.getName().equals(paramName))
+                .findFirst();
+
+        if (!existingParam.isPresent()) {
+            throw new NiFiClientException("Unable to delete parameter, no parameter found with name '" + paramName + "'");
+        }
+
+        // Construct the objects for the update, a NULL value indicates to the server to removes the parameter...
+        final ParameterDTO parameterDTO = existingParam.get();
+        parameterDTO.setValue(null);
+        parameterDTO.setDescription(null);
+        parameterDTO.setSensitive(null);
+
+        final ParameterEntity parameterEntity = new ParameterEntity();
+        parameterEntity.setParameter(parameterDTO);
+
+        final ParameterContextDTO parameterContextDTO = new ParameterContextDTO();
+        parameterContextDTO.setId(existingEntity.getId());
+        parameterContextDTO.setParameters(Collections.singleton(parameterEntity));
+
+        final ParameterContextEntity updatedParameterContextEntity = new ParameterContextEntity();
+        updatedParameterContextEntity.setId(paramContextId);
+        updatedParameterContextEntity.setComponent(parameterContextDTO);
+        updatedParameterContextEntity.setRevision(existingEntity.getRevision());
+
+        // Submit the update request...
+        final ParameterContextUpdateRequestEntity updateRequestEntity = paramContextClient.updateParamContext(updatedParameterContextEntity);
+        performUpdate(paramContextClient, updatedParameterContextEntity, updateRequestEntity);
+
+        if (isInteractive()) {
+            println();
+        }
+
+        return VoidResult.getInstance();
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/DeleteParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/DeleteParamContext.java
new file mode 100644
index 0000000..eb18a15
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/DeleteParamContext.java
@@ -0,0 +1,63 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class DeleteParamContext extends AbstractNiFiCommand<StringResult> {
+
+    public DeleteParamContext() {
+        super("delete-param-context", StringResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Deletes a parameter context.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+    }
+
+    @Override
+    public StringResult doExecute(NiFiClient client, Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+        final String paramContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity existingParamContext = paramContextClient.getParamContext(paramContextId);
+
+        final String version = String.valueOf(existingParamContext.getRevision().getVersion());
+        paramContextClient.deleteParamContext(paramContextId, version);
+        return new StringResult(paramContextId, isInteractive());
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ExportParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ExportParamContext.java
new file mode 100644
index 0000000..28fde1f
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ExportParamContext.java
@@ -0,0 +1,130 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.Validate;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.WritableResult;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.util.JacksonUtils;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.dto.ParameterDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterEntity;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Comparator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class ExportParamContext extends AbstractNiFiCommand<ExportParamContext.ExportedParamContextResult> {
+
+    public ExportParamContext() {
+        super("export-param-context", ExportedParamContextResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Exports a given parameter context to a json representation, with the option of writing to a file. ";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+        addOption(CommandOption.OUTPUT_FILE.createOption());
+    }
+
+    @Override
+    public ExportedParamContextResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+        final String paramContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+        final String outputFilename = getArg(properties, CommandOption.OUTPUT_FILE);
+
+        // retrieve the context by id
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity parameterContextEntity = paramContextClient.getParamContext(paramContextId);
+
+        // clear out values that don't make sense for importing to next environment
+        final ParameterContextDTO parameterContext = parameterContextEntity.getComponent();
+        parameterContext.setId(null);
+        parameterContext.setBoundProcessGroups(null);
+
+        for (final ParameterEntity parameterEntity : parameterContext.getParameters()) {
+            final ParameterDTO parameterDTO = parameterEntity.getParameter();
+            parameterDTO.setReferencingComponents(null);
+            if (parameterDTO.getSensitive()) {
+                parameterDTO.setValue(null);
+            }
+            parameterEntity.setCanWrite(null);
+        }
+
+        // sort the entities so that each export is in consistent order
+        final Comparator<ParameterEntity> entityComparator = (p1, p2) ->{
+            final String p1Name = p1.getParameter().getName();
+            final String p2Name = p2.getParameter().getName();
+            return p1Name.compareTo(p2Name);
+        };
+
+        final Set<ParameterEntity> sortedEntities = new TreeSet<>(entityComparator);
+        sortedEntities.addAll(parameterContext.getParameters());
+        parameterContext.setParameters(sortedEntities);
+
+        return new ExportedParamContextResult(parameterContext, outputFilename);
+    }
+
+    /**
+     * Result for writing the exported param context.
+     */
+    public static class ExportedParamContextResult implements WritableResult<ParameterContextDTO> {
+
+        private final ParameterContextDTO parameterContext;
+        private final String outputFilename;
+
+        public ExportedParamContextResult(final ParameterContextDTO parameterContext, final String outputFilename) {
+            this.parameterContext = parameterContext;
+            this.outputFilename = outputFilename;
+            Validate.notNull(this.parameterContext);
+        }
+
+        @Override
+        public void write(final PrintStream output) throws IOException {
+            if (outputFilename != null) {
+                try (final OutputStream resultOut = new FileOutputStream(outputFilename)) {
+                    JacksonUtils.write(parameterContext, resultOut);
+                }
+            } else {
+                JacksonUtils.write(parameterContext, output);
+            }
+        }
+
+        @Override
+        public ParameterContextDTO getResult() {
+            return parameterContext;
+        }
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/GetParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/GetParamContext.java
new file mode 100644
index 0000000..d1c5308
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/GetParamContext.java
@@ -0,0 +1,57 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.ParamContextResult;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class GetParamContext extends AbstractNiFiCommand<ParamContextResult> {
+
+    public GetParamContext() {
+        super("get-param-context", ParamContextResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Retrieves a parameter context by id and list each parameter and it's value.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+    }
+
+    @Override
+    public ParamContextResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+        final String paramContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity parameterContext = paramContextClient.getParamContext(paramContextId);
+        return new ParamContextResult(getResultType(properties), parameterContext);
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ImportParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ImportParamContext.java
new file mode 100644
index 0000000..04e99de
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ImportParamContext.java
@@ -0,0 +1,93 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.toolkit.cli.impl.util.JacksonUtils;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class ImportParamContext extends AbstractNiFiCommand<StringResult> {
+
+    public ImportParamContext() {
+        super("import-param-context", StringResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Imports a parameter context using the output from the export-param-context command as the context to import. " +
+                "If the context name and context description arguments are specified, they will override what is in the context json. ";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PARAM_CONTEXT_NAME.createOption());
+        addOption(CommandOption.PARAM_CONTEXT_DESC.createOption());
+        addOption(CommandOption.INPUT_SOURCE.createOption());
+    }
+
+    @Override
+    public StringResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+        // optional params
+        final String paramContextName = getArg(properties, CommandOption.PARAM_CONTEXT_NAME);
+        final String paramContextDesc = getArg(properties, CommandOption.PARAM_CONTEXT_DESC);
+
+        // read the content of the input source into memory
+        final String inputSource = getRequiredArg(properties, CommandOption.INPUT_SOURCE);
+        final String paramContextJson = getInputSourceContent(inputSource);
+
+        // unmarshall the content into the DTO object
+        final ObjectMapper objectMapper = JacksonUtils.getObjectMapper();
+        final ParameterContextDTO paramContext = objectMapper.readValue(paramContextJson, ParameterContextDTO.class);
+
+        // override context name if specified
+        if (!StringUtils.isBlank(paramContextName)) {
+            paramContext.setName(paramContextName);
+        }
+
+        // override context description if specified
+        if (!StringUtils.isBlank(paramContextDesc)) {
+            paramContext.setDescription(paramContextDesc);
+        }
+
+        // create the entity to wrap the context
+        final ParameterContextEntity paramContextEntity = new ParameterContextEntity();
+        paramContextEntity.setComponent(paramContext);
+        paramContextEntity.setRevision(getInitialRevisionDTO());
+
+        // create the context and return the id
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity createdParamContext = paramContextClient.createParamContext(paramContextEntity);
+        return new StringResult(createdParamContext.getId(), isInteractive());
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ListParamContexts.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ListParamContexts.java
new file mode 100644
index 0000000..10568cc
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/ListParamContexts.java
@@ -0,0 +1,50 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.ParamContextsResult;
+import org.apache.nifi.web.api.entity.ParameterContextsEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class ListParamContexts extends AbstractNiFiCommand<ParamContextsResult> {
+
+    public ListParamContexts() {
+        super("list-param-contexts", ParamContextsResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Lists the parameter contexts that the current user is authorized to retrieve.";
+    }
+
+    @Override
+    public ParamContextsResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+        final ParamContextClient contextClient = client.getParamContextClient();
+        final ParameterContextsEntity contexts = contextClient.getParamContexts();
+        return new ParamContextsResult(getResultType(properties), contexts);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/MergeParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/MergeParamContext.java
new file mode 100644
index 0000000..c7a99cc
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/MergeParamContext.java
@@ -0,0 +1,136 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.toolkit.cli.impl.util.JacksonUtils;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.dto.ParameterDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
+import org.apache.nifi.web.api.entity.ParameterEntity;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+
+public class MergeParamContext extends AbstractUpdateParamContextCommand<VoidResult> {
+
+    public MergeParamContext() {
+        super("merge-param-context", VoidResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Adds any parameters that exist in the exported context that don't exist in the existing context.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+        addOption(CommandOption.INPUT_SOURCE.createOption());
+    }
+
+    @Override
+    public VoidResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        final String existingContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+
+        // read the content of the input source into memory
+        final String inputSource = getRequiredArg(properties, CommandOption.INPUT_SOURCE);
+        final String paramContextJson = getInputSourceContent(inputSource);
+
+        // unmarshall the content into the DTO object
+        final ObjectMapper objectMapper = JacksonUtils.getObjectMapper();
+        final ParameterContextDTO incomingContext = objectMapper.readValue(paramContextJson, ParameterContextDTO.class);
+        if (incomingContext.getParameters() == null) {
+            incomingContext.setParameters(new LinkedHashSet<>());
+        }
+
+        // retrieve the existing context by id
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity existingContextEntity = paramContextClient.getParamContext(existingContextId);
+
+        final ParameterContextDTO existingContext = existingContextEntity.getComponent();
+        if (existingContext.getParameters() == null) {
+            existingContext.setParameters(new LinkedHashSet<>());
+        }
+
+        final Set<ParameterEntity> createdParameters = new LinkedHashSet<>();
+
+        // determine which incoming params are not in the set of existing params
+        for (final ParameterEntity incomingParameterEntity : incomingContext.getParameters()) {
+            final ParameterDTO incomingParameter = incomingParameterEntity.getParameter();
+            final String parameterName = incomingParameter.getName();
+
+            final Optional<ParameterDTO> existingParameter = existingContext.getParameters().stream()
+                    .map(p -> p.getParameter())
+                    .filter(p -> p.getName().equals(parameterName))
+                    .findFirst();
+
+            if (!existingParameter.isPresent()) {
+                final ParameterEntity createdParam = createParameter(incomingParameter);
+                createdParameters.add(createdParam);
+            }
+        }
+
+        // create a new entity to issue an update request with the newly added params
+        final ParameterContextDTO updatedContextDto = new ParameterContextDTO();
+        updatedContextDto.setId(existingContext.getId());
+        updatedContextDto.setParameters(createdParameters);
+
+        final ParameterContextEntity updatedContextEntity = new ParameterContextEntity();
+        updatedContextEntity.setId(existingContext.getId());
+        updatedContextEntity.setComponent(updatedContextDto);
+        updatedContextEntity.setRevision(existingContextEntity.getRevision());
+
+        // Submit the update request...
+        final ParameterContextUpdateRequestEntity updateRequestEntity = paramContextClient.updateParamContext(updatedContextEntity);
+        performUpdate(paramContextClient, updatedContextEntity, updateRequestEntity);
+
+        printlnIfInteractive("");
+        return VoidResult.getInstance();
+    }
+
+    private ParameterEntity createParameter(final ParameterDTO incomingParam) throws CommandException {
+        final String parameterName = incomingParam.getName();
+        printlnIfInteractive("Found parameter to add - '" + parameterName + "'");
+
+        final ParameterDTO newParameter = new ParameterDTO();
+        newParameter.setName(incomingParam.getName());
+        newParameter.setDescription(incomingParam.getDescription());
+        newParameter.setSensitive(incomingParam.getSensitive());
+        newParameter.setValue(incomingParam.getValue());
+
+        final ParameterEntity newParameterEntity = new ParameterEntity();
+        newParameterEntity.setParameter(newParameter);
+        return newParameterEntity;
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/SetParam.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/SetParam.java
new file mode 100644
index 0000000..ad1fd6d
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/params/SetParam.java
@@ -0,0 +1,129 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.params;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ParamContextClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.dto.ParameterDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
+import org.apache.nifi.web.api.entity.ParameterEntity;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Properties;
+
+public class SetParam extends AbstractUpdateParamContextCommand<VoidResult> {
+
+    public SetParam() {
+        super("set-param", VoidResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Creates or updates a parameter in the given parameter context.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+        addOption(CommandOption.PARAM_NAME.createOption());
+        addOption(CommandOption.PARAM_DESC.createOption());
+        addOption(CommandOption.PARAM_VALUE.createOption());
+        addOption(CommandOption.PARAM_SENSITIVE.createOption());
+    }
+
+    @Override
+    public VoidResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        // Required args...
+        final String paramContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+        final String paramName = getRequiredArg(properties, CommandOption.PARAM_NAME);
+
+        // Optional args...
+        final String paramValue = getArg(properties, CommandOption.PARAM_VALUE);
+        final String paramDesc = getArg(properties, CommandOption.PARAM_DESC);
+        final String paramSensitive = getArg(properties, CommandOption.PARAM_SENSITIVE);
+        if (!StringUtils.isBlank(paramSensitive) && !"true".equals(paramSensitive) && !"false".equals(paramSensitive)) {
+            throw new IllegalArgumentException("Parameter sensitive flag must be one of 'true' or 'false'");
+        }
+
+        // Ensure the context exists...
+        final ParamContextClient paramContextClient = client.getParamContextClient();
+        final ParameterContextEntity existingParameterContextEntity = paramContextClient.getParamContext(paramContextId);
+        final ParameterContextDTO existingParameterContextDTO = existingParameterContextEntity.getComponent();
+
+        // Determine if this is an existing param or a new one...
+        final Optional<ParameterDTO> existingParam = existingParameterContextDTO.getParameters().stream()
+                .map(p -> p.getParameter())
+                .filter(p -> p.getName().equals(paramName))
+                .findFirst();
+
+        if (!existingParam.isPresent() && paramValue == null) {
+            throw new IllegalArgumentException("A parameter value is required when creating a new parameter");
+        }
+
+        // Construct the objects for the update...
+        final ParameterDTO parameterDTO = existingParam.isPresent() ? existingParam.get() : new ParameterDTO();
+        parameterDTO.setName(paramName);
+
+        if (paramValue != null) {
+            parameterDTO.setValue(paramValue);
+        }
+
+        if (paramDesc != null) {
+            parameterDTO.setDescription(paramDesc);
+        }
+
+        if (!StringUtils.isBlank(paramSensitive)) {
+            parameterDTO.setSensitive(Boolean.valueOf(paramSensitive));
+        }
+
+        final ParameterEntity parameterEntity = new ParameterEntity();
+        parameterEntity.setParameter(parameterDTO);
+
+        final ParameterContextDTO parameterContextDTO = new ParameterContextDTO();
+        parameterContextDTO.setId(existingParameterContextEntity.getId());
+        parameterContextDTO.setParameters(Collections.singleton(parameterEntity));
+
+        final ParameterContextEntity updatedParameterContextEntity = new ParameterContextEntity();
+        updatedParameterContextEntity.setId(paramContextId);
+        updatedParameterContextEntity.setComponent(parameterContextDTO);
+        updatedParameterContextEntity.setRevision(existingParameterContextEntity.getRevision());
+
+        // Submit the update request...
+        final ParameterContextUpdateRequestEntity updateRequestEntity = paramContextClient.updateParamContext(updatedParameterContextEntity);
+        performUpdate(paramContextClient, updatedParameterContextEntity, updateRequestEntity);
+
+        if (isInteractive()) {
+            println();
+        }
+
+        return VoidResult.getInstance();
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetParamContext.java
new file mode 100644
index 0000000..de0d5ae
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetParamContext.java
@@ -0,0 +1,81 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.util.StringUtils;
+import org.apache.nifi.web.api.dto.ParameterContextReferenceDTO;
+import org.apache.nifi.web.api.dto.ProcessGroupDTO;
+import org.apache.nifi.web.api.entity.ProcessGroupEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class PGGetParamContext extends AbstractNiFiCommand<StringResult> {
+
+    public PGGetParamContext() {
+        super("pg-get-param-context", StringResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Gets the id of the parameter context that is bound to the given process group.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PG_ID.createOption());
+    }
+
+    @Override
+    public StringResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+        final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
+        final ProcessGroupClient pgClient = client.getProcessGroupClient();
+        final ProcessGroupEntity pgEntity = pgClient.getProcessGroup(pgId);
+
+        final String paramContextId = getParamContextId(pgEntity.getComponent());
+        return new StringResult(paramContextId, isInteractive());
+    }
+
+    private String getParamContextId(final ProcessGroupDTO processGroup) {
+        if (processGroup == null) {
+            return "";
+        }
+
+        final ParameterContextReferenceDTO parameterContextReference = processGroup.getParameterContext();
+        if (parameterContextReference == null) {
+            return "";
+        }
+
+        final String paramContextId = parameterContextReference.getId();
+        if (StringUtils.isBlank(paramContextId)) {
+            return "";
+        }
+
+        return paramContextId;
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetParamContext.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetParamContext.java
new file mode 100644
index 0000000..7cb6d9b
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGSetParamContext.java
@@ -0,0 +1,79 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.dto.ParameterContextReferenceDTO;
+import org.apache.nifi.web.api.dto.ProcessGroupDTO;
+import org.apache.nifi.web.api.entity.ProcessGroupEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class PGSetParamContext extends AbstractNiFiCommand<VoidResult> {
+
+    public PGSetParamContext() {
+        super("pg-set-param-context", VoidResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Sets the parameter context bound to the given process group.";
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.PG_ID.createOption());
+        addOption(CommandOption.PARAM_CONTEXT_ID.createOption());
+    }
+
+    @Override
+    public VoidResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+        final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
+        final String paramContextId = getRequiredArg(properties, CommandOption.PARAM_CONTEXT_ID);
+
+        final ProcessGroupClient pgClient = client.getProcessGroupClient();
+        final ProcessGroupEntity pgEntity = pgClient.getProcessGroup(pgId);
+
+        final ParameterContextReferenceDTO parameterContextReference = new ParameterContextReferenceDTO();
+        parameterContextReference.setId(paramContextId);
+
+        final ProcessGroupDTO updatedDTO = new ProcessGroupDTO();
+        updatedDTO.setId(pgId);
+        updatedDTO.setParameterContext(parameterContextReference);
+
+        final ProcessGroupEntity updatedEntity = new ProcessGroupEntity();
+        updatedEntity.setId(pgId);
+        updatedEntity.setComponent(updatedDTO);
+        updatedEntity.setRevision(pgEntity.getRevision());
+
+        pgClient.updateProcessGroup(updatedEntity);
+        return VoidResult.getInstance();
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java
index 5f8cd89..49e4dcd 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java
@@ -18,7 +18,6 @@ package org.apache.nifi.toolkit.cli.impl.command.registry.flow;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.commons.cli.ParseException;
-import org.apache.commons.io.IOUtils;
 import org.apache.nifi.registry.client.FlowClient;
 import org.apache.nifi.registry.client.FlowSnapshotClient;
 import org.apache.nifi.registry.client.NiFiRegistryClient;
@@ -33,11 +32,6 @@ import org.apache.nifi.toolkit.cli.impl.result.StringResult;
 import org.apache.nifi.toolkit.cli.impl.util.JacksonUtils;
 
 import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Paths;
 import java.util.Properties;
 
 /**
@@ -67,16 +61,7 @@ public class ImportFlowVersion extends AbstractNiFiRegistryCommand<StringResult>
         final String flowId = getRequiredArg(properties, CommandOption.FLOW_ID);
         final String inputFile = getRequiredArg(properties, CommandOption.INPUT_SOURCE);
 
-        String contents;
-        try {
-            // try a public resource URL
-            URL url = new URL(inputFile);
-            contents = IOUtils.toString(url, StandardCharsets.UTF_8);
-        } catch (MalformedURLException e) {
-            // assume a local file then
-            URI uri = Paths.get(inputFile).toAbsolutePath().toUri();
-            contents = IOUtils.toString(uri, StandardCharsets.UTF_8);
-        }
+        final String contents = getInputSourceContent(inputFile);
 
         final FlowClient flowClient = client.getFlowClient();
         final FlowSnapshotClient snapshotClient = client.getFlowSnapshotClient();
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/ParamContextResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/ParamContextResult.java
new file mode 100644
index 0000000..b0e49af
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/ParamContextResult.java
@@ -0,0 +1,82 @@
+/*
+ * 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.toolkit.cli.impl.result.nifi;
+
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter;
+import org.apache.nifi.toolkit.cli.impl.result.writer.Table;
+import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.dto.ParameterDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterEntity;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class ParamContextResult extends AbstractWritableResult<ParameterContextEntity> {
+
+    private final ParameterContextEntity parameterContext;
+
+    public ParamContextResult(final ResultType resultType, final ParameterContextEntity parameterContext) {
+        super(resultType);
+        this.parameterContext = parameterContext;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) throws IOException {
+        final ParameterContextDTO parameterContextDTO = parameterContext.getComponent();
+
+        final Set<ParameterEntity> paramEntities = parameterContextDTO.getParameters() == null
+                ? Collections.emptySet() : parameterContextDTO.getParameters();
+
+        final Set<ParameterDTO> paramDTOs = paramEntities.stream()
+                .map(p -> p.getParameter())
+                .collect(Collectors.toSet());
+
+        final List<ParameterDTO> sortedParams =paramDTOs.stream()
+                .sorted(Comparator.comparing(ParameterDTO::getName))
+                .collect(Collectors.toList());
+
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Name", 20, 60, false)
+                .column("Value", 20, 80, false)
+                .column("Sensitive", 10, 10, false)
+                .column("Description", 20, 80, true)
+                .build();
+
+        for (int i = 0; i < sortedParams.size(); i++) {
+            final ParameterDTO r = sortedParams.get(i);
+            table.addRow(String.valueOf(i+1), r.getName(), r.getValue(), r.getSensitive().toString(), r.getDescription());
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+    @Override
+    public ParameterContextEntity getResult() {
+        return parameterContext;
+    }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/ParamContextsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/ParamContextsResult.java
new file mode 100644
index 0000000..ceeac27
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/ParamContextsResult.java
@@ -0,0 +1,118 @@
+/*
+ * 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.toolkit.cli.impl.result.nifi;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.ReferenceResolver;
+import org.apache.nifi.toolkit.cli.api.Referenceable;
+import org.apache.nifi.toolkit.cli.api.ResolvedReference;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter;
+import org.apache.nifi.toolkit.cli.impl.result.writer.Table;
+import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter;
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.entity.ParameterContextsEntity;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ParamContextsResult extends AbstractWritableResult<ParameterContextsEntity> implements Referenceable {
+
+    private final ParameterContextsEntity parameterContexts;
+    private final List<ParameterContextDTO> results;
+
+    public ParamContextsResult(final ResultType resultType, final ParameterContextsEntity parameterContexts) {
+        super(resultType);
+        this.parameterContexts = Validate.notNull(parameterContexts);
+        this.results = new ArrayList<>();
+
+        // If there is a param context that the user doesn't have permissions to then the entity will be returned with
+        // a null component so we need to create a place holder DTO that has only the id populated
+        Optional.ofNullable(parameterContexts.getParameterContexts()).orElse(Collections.emptySet())
+                .forEach(pc -> {
+                    if (pc.getComponent() == null) {
+                        final ParameterContextDTO dto = new ParameterContextDTO();
+                        dto.setId(pc.getId());
+                        dto.setName(pc.getId());
+                        results.add(dto);
+                    } else {
+                        results.add(pc.getComponent());
+                    }
+                });
+
+        // NOTE: it is important that the order the contexts are printed is the same order for the ReferenceResolver
+        Collections.sort(results, Comparator.comparing(ParameterContextDTO::getName));
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) throws IOException {
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Id", 36, 36, false)
+                .column("Name", 20, 60, true)
+                .column("Description", 40, 60, true)
+                .build();
+
+        for (int i = 0; i < results.size(); i++) {
+            final ParameterContextDTO r = results.get(i);
+            table.addRow("" + (i+1), r.getId(), r.getName(), r.getDescription());
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+    @Override
+    public ParameterContextsEntity getResult() {
+        return parameterContexts;
+    }
+
+    @Override
+    public ReferenceResolver createReferenceResolver(final Context context) {
+        final Map<Integer, ParameterContextDTO> backRefs = new HashMap<>();
+        final AtomicInteger position = new AtomicInteger(0);
+        results.forEach(pc -> backRefs.put(position.incrementAndGet(), pc));
+
+        return new ReferenceResolver() {
+            @Override
+            public ResolvedReference resolve(final CommandOption option, final Integer position) {
+                final ParameterContextDTO parameterContext = backRefs.get(position);
+                if (parameterContext != null) {
+                    return new ResolvedReference(option, position, parameterContext.getName(), parameterContext.getId());
+                } else {
+                    return null;
+                }
+            }
+
+            @Override
+            public boolean isEmpty() {
+                return backRefs.isEmpty();
+            }
+        };
+    }
+}