python4cpm 1.0.21__tar.gz → 1.0.23__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python4cpm
3
- Version: 1.0.21
3
+ Version: 1.0.23
4
4
  Summary: Python for CPM
5
5
  Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
6
- License: MIT License
7
-
8
- Copyright (c) 2026 Gonzalo Atienza Rela
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in
18
- all copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
- THE SOFTWARE.
27
-
6
+ License-Expression: MIT
28
7
  Requires-Python: >=3.10
29
8
  Description-Content-Type: text/markdown
30
9
  License-File: LICENSE
@@ -32,7 +11,7 @@ Dynamic: license-file
32
11
 
33
12
  # Python4CPM
34
13
 
35
- A simple way of using python scripts with CyberArk CPM rotations. This module levereages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
14
+ A simple way of using python scripts with CyberArk CPM/SRS rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
36
15
 
37
16
  This platform allows you to duplicate it multiple times, simply changing its settings from Privilege Cloud/PVWA to point to different python scripts leveraging the module `python4cpm`.
38
17
 
@@ -40,14 +19,14 @@ This platform allows you to duplicate it multiple times, simply changing its set
40
19
 
41
20
  ### Preparing Python
42
21
 
43
- 1. Install Python in CPM.
22
+ 1. Install Python along CPM or the SRS Connector Management Agent.
44
23
  - **Python must be installed for all users**. Follow the custom install steps from the installation wizard to check the checkbox.
45
- 3. Create a venv in CPM, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
24
+ 3. Create a venv in the server, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
46
25
  4. Install `python4cpm` in your venv:
47
26
  - If your CPM can connect to the internet, install with `c:\venv\Scripts\pip install python4cpm`.
48
27
  - If your CPM cannot connect to the internet:
49
28
  - Download the latest `python4cpm-*.whl` file from the [pypi project files](https://pypi.org/project/python4cpm/#files).
50
- - Copy the file to CPM and extract to a temporary directory called `python4cpm-wheel`.
29
+ - Copy the file to the server into a temporary directory called `python4cpm-wheel`.
51
30
  - From the parent directory of `python4cpm-wheel` run `c:\venv\Scripts\pip install --no-index --find-links=.\python4cpm-wheel python4cpm`.
52
31
 
53
32
 
@@ -78,7 +57,127 @@ This platform allows you to duplicate it multiple times, simply changing its set
78
57
 
79
58
  ## Python Script
80
59
 
81
- ### Example:
60
+ ### Using the handler (recommended):
61
+
62
+ ```python
63
+ from python4cpm import Python4CPMHandler
64
+
65
+
66
+ class MyRotator(Python4CPMHandler): # create a subclass for the Handler
67
+ """
68
+ These are the usable properties and methods from Python4CPMHandler:
69
+
70
+ self.args.action # action requested from CPM/SRS
71
+ self.args.address # address from the account address field
72
+ self.args.username # username from the account username field
73
+ self.args.reconcile_username # reconcile username from the linked reconcile account
74
+ self.args.logon_username # logon username from the linked logon account
75
+ self.args.logging # used to carry the platform logging settings for python
76
+ self.secrets.password.get() # get str from password received from the vault
77
+ self.secrets.new_password.get() # get str from new password in case of a rotation
78
+ self.secrets.logon_password.get() # get str from linked logon account password
79
+ self.secrets.reconcile_password.get() # get str from linked reconcile account password
80
+
81
+ Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
82
+
83
+ self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyRotator.log
84
+ self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyRotator.log
85
+ self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyRotator.log
86
+
87
+ Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
88
+
89
+ self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyRotator.log if logging level is set to debug
90
+
91
+ =============================
92
+ REQUIRED TERMINATION SIGNALS
93
+ =============================
94
+ Terminate signals -> MUST use one of the following three signals to terminate the script:
95
+
96
+ self.close_success() # terminate with success state
97
+ self.close_fail() # terminate with recoverable failed state
98
+ self.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
99
+
100
+ When calling a signal sys.exit is invoked and the script is terminated. If no signal is called, and the script finishes without any exception, it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
101
+ =============================
102
+ =============================
103
+ """
104
+
105
+ # =============================
106
+ # REQUIRED METHODS (MUST DEFINE)
107
+ # =============================
108
+ # verify(), logon(), change(), prereconcile(), reconcile()
109
+
110
+ def verify(self):
111
+ self._verify()
112
+ self.log_info("verification successful")
113
+ self.close_success()
114
+
115
+ def logon(self):
116
+ self.close_success() # terminate with success state if nothing needs to be done with a given action.
117
+
118
+ def change(self):
119
+ self._change()
120
+ self.log_error("something went wrong")
121
+ self.close_fail()
122
+
123
+ def prereconcile(self):
124
+ self._verify(from_reconcile=True)
125
+ self.close_success()
126
+
127
+ def reconcile(self):
128
+ self._change(from_reconcile=True)
129
+ self.close_success()
130
+
131
+ def _verify(self, from_reconcile=False):
132
+ if from_reconcile is False:
133
+ pass
134
+ # TODO: use self.args.address, self.args.username, self.secrets.password.get()
135
+ # for your logic in a verification
136
+ else:
137
+ pass
138
+ # TODO: use self.args.address, self.args.reconcile_username, self.secrets.reconcile_password.get()
139
+ # for your logic in a verification
140
+ result = True
141
+ if result is True:
142
+ self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyRotator.log
143
+ else:
144
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
145
+ self.close_fail()
146
+
147
+ def _change(self, from_reconcile=False):
148
+ if from_reconcile is False:
149
+ pass
150
+ # TODO: use self.args.address, self.args.username, self.secrets.password.get()
151
+ # and self.secrets.new_password.get() for your logic in a rotation
152
+ else:
153
+ pass
154
+ # TODO: use self.args.address, self.args.username, self.args.reconcile_username,
155
+ # self.secrets.reconcile_password.get() and self.secrets.new_password.get() for your logic in a reconciliation
156
+ result = True
157
+ if result is True:
158
+ self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyRotator.log
159
+ else:
160
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
161
+ self.close_fail()
162
+
163
+
164
+ if __name__ == "__main__":
165
+ MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
166
+ ```
167
+ (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpmhandler).
168
+
169
+ When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
170
+ 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
171
+ 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
172
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
173
+ 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
174
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
175
+ 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
176
+ 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return empty strings.
177
+ 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return empty strings.
178
+
179
+
180
+ ### Using Python4CPM properties and methods directly (for low level controls):
82
181
 
83
182
  ```python
84
183
  from python4cpm import Python4CPM
@@ -87,7 +186,7 @@ from python4cpm import Python4CPM
87
186
  p4cpm = Python4CPM("MyApp") # this instantiates the object and grabs all arguments and secrets shared by the .NET SDK
88
187
 
89
188
  # These are the usable properties and related methods from the object:
90
- p4cpm.args.action # action requested from CPM
189
+ p4cpm.args.action # action requested from CPM/SRS
91
190
  p4cpm.args.address # address from the account address field
92
191
  p4cpm.args.username # username from the account username field
93
192
  p4cpm.args.reconcile_username # reconcile username from the linked reconcile account
@@ -98,36 +197,35 @@ p4cpm.secrets.new_password.get() # get str from new password in case of a rotati
98
197
  p4cpm.secrets.logon_password.get() # get str from linked logon account password
99
198
  p4cpm.secrets.reconcile_password.get() # get str from linked reconcile account password
100
199
 
101
- # Logging methods -> Will only log if Automatic Platform Management -> Additional Policy Settings -> Parameters -> PythonLogging is set to yes (default is yes)
102
- p4cpm.log_error("this is an error message") # logs error into Logs/ThirdParty/Python4CPM/MyApp.log
103
- p4cpm.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/Python4CPM/MyApp.log
104
- p4cpm.log_info("this is an info message") # logs info into Logs/ThirdParty/Python4CPM/MyApp.log
105
- # Logging level -> Will only log debug messages if Automatic Platform Management -> Additional Policy Settings -> Parameters -> PythonLoggingLevel is set to debug (default is info)
106
- p4cpm.log_debug("this is an debug message") # logs info into Logs/ThirdParty/Python4CPM/MyApp.log if logging level is set to debug
200
+ # Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
201
+ p4cpm.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
202
+ p4cpm.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
203
+ p4cpm.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
204
+ # Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
205
+ p4cpm.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
107
206
 
108
207
  # Terminate signals -> MUST use one of the following three signals to terminate the script:
109
208
  ## p4cpm.close_success() # terminate with success state
110
209
  ## p4cpm.close_fail() # terminate with recoverable failed state
111
210
  ## p4cpm.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
112
- # When calling a signal sys.exit is invoked and the script is terminated.
113
- # If no signal is called, and the script finishes without any exception, it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
211
+ # When calling a signal sys.exit is invoked and the script is terminated. If no signal is called, and the script finishes without any exception, it will behave like p4cpm.close_fail(unrecoverable=True) and log an error message.
114
212
 
115
213
 
116
214
  # Verification example -> verify the username and password are valid
117
215
  def verify(from_reconcile=False):
118
216
  if from_reconcile is False:
119
217
  pass
120
- # Use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
218
+ # TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
121
219
  # for your logic in a verification
122
220
  else:
123
221
  pass
124
- # Use p4cpm.args.address, p4cpm.args.reconcile_username, p4cpm.secrets.reconcile_password.get()
222
+ # TODO: use p4cpm.args.address, p4cpm.args.reconcile_username, p4cpm.secrets.reconcile_password.get()
125
223
  # for your logic in a verification
126
224
  result = True
127
225
  if result is True:
128
- p4cpm.log_info("verification successful") # logs info message into Logs/ThirdParty/Python4CPM/MyApp.log
226
+ p4cpm.log_info("verification successful")
129
227
  else:
130
- p4cpm.log_error("something went wrong") # logs error message Logs/ThirdParty/Python4CPM/MyApp.log
228
+ p4cpm.log_error("something went wrong")
131
229
  raise Exception("verify failed") # raise to trigger failed termination signal
132
230
 
133
231
 
@@ -135,17 +233,17 @@ def verify(from_reconcile=False):
135
233
  def change(from_reconcile=False):
136
234
  if from_reconcile is False:
137
235
  pass
138
- # Use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
236
+ # TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.secrets.password.get()
139
237
  # and p4cpm.secrets.new_password.get() for your logic in a rotation
140
238
  else:
141
239
  pass
142
- # Use p4cpm.args.address, p4cpm.args.username, p4cpm.args.reconcile_username,
240
+ # TODO: use p4cpm.args.address, p4cpm.args.username, p4cpm.args.reconcile_username,
143
241
  # p4cpm.secrets.reconcile_password.get() and p4cpm.secrets.new_password.get() for your logic in a reconciliation
144
242
  result = True
145
243
  if result is True:
146
- p4cpm.log_info("rotation successful") # logs info message into Logs/ThirdParty/Python4CPM/MyApp.log
244
+ p4cpm.log_info("rotation successful")
147
245
  else:
148
- p4cpm.log_error("something went wrong") # logs error message Logs/ThirdParty/Python4CPM/MyApp.log
246
+ p4cpm.log_error("something went wrong")
149
247
  raise Exception("change failed") # raise to trigger failed termination signal
150
248
 
151
249
 
@@ -153,48 +251,44 @@ if __name__ == "__main__":
153
251
  try:
154
252
  if p4cpm.args.action == Python4CPM.ACTION_VERIFY: # class attribute ACTION_VERIFY holds the verify action value
155
253
  verify()
156
- p4cpm.close_success() # terminate with success state
254
+ p4cpm.close_success()
157
255
  elif p4cpm.args.action == Python4CPM.ACTION_LOGON: # class attribute ACTION_LOGON holds the logon action value
158
- verify()
159
- p4cpm.close_success() # terminate with success state
256
+ p4cpm.close_success() # terminate with success state if nothing needs to be done with a given action.
160
257
  elif p4cpm.args.action == Python4CPM.ACTION_CHANGE: # class attribute ACTION_CHANGE holds the password change action value
161
258
  change()
162
- p4cpm.close_success() # terminate with success state
259
+ p4cpm.close_success()
163
260
  elif p4cpm.args.action == Python4CPM.ACTION_PRERECONCILE: # class attribute ACTION_PRERECONCILE holds the pre-reconcile action value
164
261
  verify(from_reconcile=True)
165
- p4cpm.close_success() # terminate with success state
262
+ p4cpm.close_success()
166
263
  # Alternatively ->
167
264
  ## p4cpm.log_error("reconciliation is not supported") # let the logs know that reconciliation is not supported
168
- ## p4cpm.close_fail() # let CPM know to check the logs
265
+ ## p4cpm.close_fail() # let CPM/SRS know to check the logs
169
266
  elif p4cpm.args.action == Python4CPM.ACTION_RECONCILE: # class attribute ACTION_RECONCILE holds the reconcile action value
170
267
  change(from_reconcile=True)
171
- p4cpm.close_success() # terminate with success state
268
+ p4cpm.close_success()
172
269
  # Alternatively ->
173
270
  ## p4cpm.log_error("reconciliation is not supported") # let the logs know that reconciliation is not supported
174
- ## p4cpm.close_fail() # let CPM know to check the logs
175
- else:
176
- p4cpm.log_error(f"invalid action: '{p4cpm.args.action}'") # logs into Logs/ThirdParty/Python4CPM/MyApp.log
177
- p4cpm.close_fail(unrecoverable=True) # terminate with unrecoverable failed state
271
+ ## p4cpm.close_fail() # let CPM/SRS know to check the logs
178
272
  except Exception as e:
179
273
  p4cpm.log_error(f"{type(e).__name__}: {e}")
180
- p4cpm.close_fail()
274
+ raise e # CPM/SRS will see any Exception as a p4cpm.close_fail(unrecoverable=True)
181
275
  ```
182
- (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples).
276
+ (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpm).
183
277
 
184
278
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
185
279
  1. Verify -> the sciprt will be executed once with the `p4cpm.args.action` as `Python4CPM.ACTION_VERIFY`.
186
280
  2. Change -> the sciprt will be executed twice, once with the action `p4cpm.args.action` as `Python4CPM.ACTION_LOGON` and once as `Python4CPM.ACTION_CHANGE`.
187
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, it defaults to a successful return.
281
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
188
282
  3. Reconcile -> the sciprt will be executed twice, once with the `p4cpm.args.action` as `Python4CPM.ACTION_PRERECONCILE` and once as `Python4CPM.ACTION_RECONCILE`.
189
- - If all actions are not terminated with `p4cpm.close_success()` the overall reconcile will fail.
283
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
190
284
  4. When `p4cpm.args.action` comes as `Python4CPM.ACTION_VERIFY`, `Python4CPM.ACTION_LOGON` or `Python4CPM.ACTION_PRERECONCILE`: `p4cpm.secrets.new_password.get()` will always return an empty string.
191
- 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return an empty string.
192
- 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return an empty string.
285
+ 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return empty strings.
286
+ 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return empty strings.
193
287
 
194
288
 
195
- ### Installing dependancies in python venv
289
+ ### Installing dependencies in python venv
196
290
 
197
- As with any python venv, you can install dependancies in your venv.
291
+ As with any python venv, you can install dependencies in your venv.
198
292
  1. If your CPM can connect to the internet:
199
293
  - You can use regular pip install commands (e.g., `c:\venv\Scripts\pip.exe install requests`).
200
294
  2. If your CPM cannot connect to the internet:
@@ -203,7 +297,7 @@ As with any python venv, you can install dependancies in your venv.
203
297
 
204
298
  ## Dev Helper:
205
299
 
206
- For dev purposes, `NETHelper` is a companion helper that simplifies the instantiation of the `Python4CPM` object by simulating how the plugin passes arguments and secrets to `Python4CPM`.
300
+ For dev purposes, `NETHelper` is a companion helper that simplifies the instantiation of the `Python4CPM` or `Python4CPMHandler` objects by simulating how the plugin passes arguments and secrets to the modules.
207
301
  Install this module (in a dev workstation) with:
208
302
 
209
303
  ```bash
@@ -214,8 +308,10 @@ pip install python4cpm
214
308
 
215
309
  ### Example:
216
310
 
311
+ #### Set your arguments and secrets:
312
+
217
313
  ```python
218
- from python4cpm import NETHelper, Python4CPM
314
+ from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
219
315
  from getpass import getpass
220
316
 
221
317
  # Get secrets for your password, logon account password, reconcile account password and new password
@@ -225,7 +321,7 @@ logon_password = getpass("logon_password: ") # password from linked logon accoun
225
321
  reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
226
322
  new_password = getpass("new_password: ") # new password for the rotation
227
323
 
228
- p4cpm = NETHelper.run(
324
+ NETHelper.set(
229
325
  action=Python4CPM.ACTION_LOGON, # use actions from Python4CPM.ACTION_*
230
326
  address="myapp.corp.local", # populate with the address from your account properties
231
327
  username="jdoe", # populate with the username from your account properties
@@ -238,19 +334,49 @@ p4cpm = NETHelper.run(
238
334
  reconcile_password=reconcile_password,
239
335
  new_password=new_password
240
336
  )
337
+ ```
338
+
339
+ #### Using the handler (recommended):
340
+
341
+ ```python
342
+ class MyRotator(Python4CPMHandler):
343
+ def verify(self):
344
+ # TODO: Add your logic here
345
+ self.close_success()
346
+
347
+ def logon(self):
348
+ # TODO: Add your logic here
349
+ self.close_success()
241
350
 
242
- # Use the p4cpm object during dev to build your script logic
351
+ def change(self):
352
+ # TODO: Add your logic here
353
+ self.close_success()
354
+
355
+ def prereconcile(self):
356
+ # TODO: Add your logic here
357
+ self.close_success()
358
+
359
+ def reconcile(self):
360
+ # TODO: Add your logic here
361
+ self.close_success()
362
+
363
+ MyRotator().run()
364
+ ```
365
+
366
+ #### Using Python4CPM properties and methods directly:
367
+
368
+ ```python
369
+ p4cpm = NETHelper.get()
370
+
371
+ # TODO: use the p4cpm object during dev to build your script logic
243
372
  assert password == p4cpm.secrets.password.get()
244
373
  p4cpm.log_info("success!")
245
374
  p4cpm.close_success()
246
-
247
- # Remember for your final script:
248
- ## changing the definition of p4cpm from NETHelper.run() to Python4CPM("MyApp")
249
- ## remove any secrets prompting
250
- ## remove the NETHelper import
251
375
  ```
252
376
 
253
- Remember for your final script:
254
- - Change the definition of `p4cpm` from `p4cpm = NETHelper.run(**kwargs)` to `p4cpm = Python4CPM("MyApp")`.
255
- - Remove any secrets prompting or interactive interruptions.
377
+ #### Remember for your final script:
378
+
256
379
  - Remove the import of `NETHelper`.
380
+ - Remove the `NETHelper.set()` call.
381
+ - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
382
+ - Remove any secrets prompting or interactive interruptions.