From 19758b00f04dfc5d5da4b6de52477743107a9f45 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 18:25:12 -0600 Subject: [PATCH 01/13] docs: restructure documentation layout - Rename getting-started/ to tutorials/ (tutorial content only) - Create concepts/ with architecture and feature-flags (conceptual content) - Move development-workflow and testing-and-debugging to guides/ - Move compatibility.md to reference/ - Rename SampleScripts/ to samples/ with per-sample directories - Separate templates/ to top-level directory - Create quick-start/ directory for upcoming quick-start guides --- SampleScripts/HTTP/README.md | 171 ------------------ SampleScripts/README.md | 124 ------------- SampleScripts/SSH/README.md | 5 - .../overview.md => concepts/architecture.md} | 0 docs/{guides => concepts}/feature-flags.md | 0 docs/guides/.gitkeep | 0 .../development-workflow.md | 0 .../testing-and-debugging.md | 0 docs/{ => reference}/compatibility.md | 0 .../your-first-form-script.md | 0 .../your-first-http-script.md | 0 .../your-first-ssh-script.md | 0 .../http/facebook}/CustomFacebook.json | 0 .../forgerock-openam}/Forgerock_OpenAM.json | 0 ...ithDiscoveryAndGroupMembershipRestore.json | 0 .../onelogin-jit}/OneLogin_GRC_JIT_addon.json | 0 .../assets/http_oneloginjit_1.mp4 | Bin .../assets/http_oneloginjit_1.png | Bin .../assets/http_oneloginjit_10.png | Bin .../assets/http_oneloginjit_11.png | Bin .../assets/http_oneloginjit_12.png | Bin .../assets/http_oneloginjit_13.png | Bin .../assets/http_oneloginjit_14.png | Bin .../assets/http_oneloginjit_15.png | Bin .../assets/http_oneloginjit_16.png | Bin .../assets/http_oneloginjit_17.png | Bin .../assets/http_oneloginjit_18.png | Bin .../assets/http_oneloginjit_19.png | Bin .../assets/http_oneloginjit_2.png | Bin .../assets/http_oneloginjit_20.png | Bin .../assets/http_oneloginjit_21.png | Bin .../assets/http_oneloginjit_22.png | Bin .../assets/http_oneloginjit_23.png | Bin .../assets/http_oneloginjit_3.png | Bin .../assets/http_oneloginjit_4.png | Bin .../assets/http_oneloginjit_5.png | Bin .../assets/http_oneloginjit_6.png | Bin .../assets/http_oneloginjit_7.png | Bin .../assets/http_oneloginjit_8.png | Bin .../assets/http_oneloginjit_9.png | Bin .../http/twitter}/CustomTwitter.json | 0 .../http/wordpress}/WordPressHttp.json | 0 .../GenericLinuxWithSSHKeySupport.json | 0 .../GenericLinuxWithAD.json | 0 .../GenericLinuxWithDiscovery.json | 0 .../ssh/generic-linux}/GenericLinux.json | 0 .../LinuxApplicationTextConfig.json | 0 .../LinuxSshBatchModeExample.json | 0 .../RestrictedAuthorizedKeyExample.json | 0 .../vCenterServerAppliance.json | 0 .../cisco-ios}/GenericCiscoIosTelnet.json | 0 .../racf-tn3270}/GenericRacfTn3270.json | 0 .../Pattern-GenericHttpAccountDiscovery.json | 0 .../Pattern-GenericHttpJitElevation.json | 0 .../Pattern-GenericLinuxDependentSystem.json | 0 .../Pattern-GenericLinuxFileManagement.json | 0 .../Pattern-GenericLinuxFull.json | 0 .../Pattern-GenericLinuxServiceDiscovery.json | 0 .../Pattern-GenericRestApiBasicAuth.json | 0 .../Pattern-GenericRestApiBearerToken.json | 0 .../Pattern-GenericRestApiKeyRotation.json | 0 .../Pattern-WindowsSshBasic.json | 0 .../Templates => templates}/README.md | 0 .../TemplateHttpMinimal.json | 0 .../TemplateSshMinimal.json | 0 65 files changed, 300 deletions(-) delete mode 100644 SampleScripts/HTTP/README.md delete mode 100644 SampleScripts/README.md delete mode 100644 SampleScripts/SSH/README.md rename docs/{getting-started/overview.md => concepts/architecture.md} (100%) rename docs/{guides => concepts}/feature-flags.md (100%) delete mode 100644 docs/guides/.gitkeep rename docs/{getting-started => guides}/development-workflow.md (100%) rename docs/{getting-started => guides}/testing-and-debugging.md (100%) rename docs/{ => reference}/compatibility.md (100%) rename docs/{getting-started => tutorials}/your-first-form-script.md (100%) rename docs/{getting-started => tutorials}/your-first-http-script.md (100%) rename docs/{getting-started => tutorials}/your-first-ssh-script.md (100%) rename {SampleScripts/HTTP => samples/http/facebook}/CustomFacebook.json (100%) rename {SampleScripts/HTTP => samples/http/forgerock-openam}/Forgerock_OpenAM.json (100%) rename {SampleScripts/HTTP => samples/http/okta-discovery}/Okta_WithDiscoveryAndGroupMembershipRestore.json (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/OneLogin_GRC_JIT_addon.json (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_1.mp4 (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_1.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_10.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_11.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_12.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_13.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_14.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_15.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_16.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_17.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_18.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_19.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_2.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_20.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_21.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_22.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_23.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_3.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_4.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_5.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_6.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_7.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_8.png (100%) rename {SampleScripts/HTTP => samples/http/onelogin-jit}/assets/http_oneloginjit_9.png (100%) rename {SampleScripts/HTTP => samples/http/twitter}/CustomTwitter.json (100%) rename {SampleScripts/HTTP => samples/http/wordpress}/WordPressHttp.json (100%) rename {SampleScripts/SSH => samples/ssh/generic-linux-ssh-keys}/GenericLinuxWithSSHKeySupport.json (100%) rename {SampleScripts/SSH => samples/ssh/generic-linux-with-ad}/GenericLinuxWithAD.json (100%) rename {SampleScripts/SSH => samples/ssh/generic-linux-with-discovery}/GenericLinuxWithDiscovery.json (100%) rename {SampleScripts/SSH => samples/ssh/generic-linux}/GenericLinux.json (100%) rename {SampleScripts/SSH => samples/ssh/linux-app-text-config}/LinuxApplicationTextConfig.json (100%) rename {SampleScripts/SSH => samples/ssh/linux-ssh-batch-mode}/LinuxSshBatchModeExample.json (100%) rename {SampleScripts/SSH => samples/ssh/restricted-authorized-key}/RestrictedAuthorizedKeyExample.json (100%) rename {SampleScripts/SSH => samples/ssh/vcenter-appliance}/vCenterServerAppliance.json (100%) rename {SampleScripts/Telnet => samples/telnet/cisco-ios}/GenericCiscoIosTelnet.json (100%) rename {SampleScripts/Telnet => samples/telnet/racf-tn3270}/GenericRacfTn3270.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericHttpAccountDiscovery.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericHttpJitElevation.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericLinuxDependentSystem.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericLinuxFileManagement.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericLinuxFull.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericLinuxServiceDiscovery.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericRestApiBasicAuth.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericRestApiBearerToken.json (100%) rename {SampleScripts/Templates => templates}/Pattern-GenericRestApiKeyRotation.json (100%) rename {SampleScripts/Templates => templates}/Pattern-WindowsSshBasic.json (100%) rename {SampleScripts/Templates => templates}/README.md (100%) rename {SampleScripts/Templates => templates}/TemplateHttpMinimal.json (100%) rename {SampleScripts/Templates => templates}/TemplateSshMinimal.json (100%) diff --git a/SampleScripts/HTTP/README.md b/SampleScripts/HTTP/README.md deleted file mode 100644 index 1c7cf91..0000000 --- a/SampleScripts/HTTP/README.md +++ /dev/null @@ -1,171 +0,0 @@ -[← Sample Scripts](../README.md) - -# Guide to SafeguardCustomPlatform - HTTP Scripts - -## Table of Contents -[OneLogin_GRC_JIT_addon](#onelogin_grc_jit_addon) -[Okta_WithDiscoveryAndGroupMembershipRestore](#okta_withdiscoveryandgroupmembershiprestore) - -## OneLogin_GRC_JIT_addon - -This Solution Accelerator addon was created to implement JIT role elevation for OneLogin until it is available out-of-box in Safeguard. - -### How does it work -The OneLogin_GRC_JIT_addon implements Restore/Suspend and Elevate/Demote functions. The ChangePassword function is also defined as it's a must-have for Custom Platform scripts, however it does nothing (only logs that it does nothing). - -Changing the password is: - * Either not necessary as the Account will only store a TOTP code, configured automatically by OneLogin. - * Or if the base privileged Account is created & managed by the out-of-box Starling Connect connector for OneLogin, that will be managing the password. - -The addon will be the Platform Types for additionally created Assets in Safeguard, each separate Asset representing a Role in OneLogin: - -![SafeguardCustomPlatform](assets/http_oneloginjit_1.png) - -Each privileged OneLogin User having permission to elevate into that Role needs to have an Account object created within the Asset representing the Role. - -![SafeguardCustomPlatform](assets/http_oneloginjit_2.png) - -(Note: the Assets could also represent groups of Roles.) - -In SPP the Account shows up on the Access Request portal only if it has the password set. Hence each of these Accounts need to have a dummy password configured. - -The Users need to have Entitlements / Access Request Policies to the base privileged OneLogin Account as well as for the individual Accounts representing the Roles. This requires creating an Entitlement per User as at the time of writing this reamde (in SPP v8.2) the Accounts of neither the OneLogin platform Asset, nor the custom platform Assets can be added as Linked Accounts. - -![SafeguardCustomPlatform](assets/http_oneloginjit_3.png) - -When the User is requesting access to the privileged OneLogin Account, at the same time the desired Roles should also be selected. The privileged OneLogin Account will have the Roles assigned, once the subsequent access requests representing the Roles become available (after Pending Restore state). - -![SafeguardCustomPlatform](assets/http_oneloginjit_4.png) - -#### Demo video - -Watch demo video - - -### About enabling/disabling the OneLogin user via a Safeguard Access Request -JIT enable/disable or elevate/demote tasks are implemented on the OneLogin_GRC_JIT_addon Asset/Accounts. - -There are two typical setups: - -1. When the Account objects are managed by OneLogin, all of them stored under a OneLogin_GRC_JIT_addon Asset: - * In this scenario enable the *Suspend account when checked in* function under Password Profile > Change Password policy for the main privileged account. Do not configure JIT groups for this Accounts. - * Configure the JIT group elevation for the Accounts on the Assets representing the Roles. Do not enable the *Suspend account when checked in* function for these Accounts. - * Once the main -adm account is checked out, it gets activated. The Roles get assigned depending on which corresponding Asset is requested along with the main Account. Whilst the main Account is checked out, the User can request further Roles, or check any of them in, demoting that Role in OneLogin. - -2. When the main Account is managed by the out-of-box Starling Connect connector for OneLogin, and only the Assets representing a Role are configured with the OneLogin_GRC_JIT_addon platform type: - * In this case both the *Suspend account when checked in* function and the JIT groups should be configured on each of the Asset/Accounts representing a OneLogin Role. - * Despite the main Account is checked out, it is still inactive in OneLogin. It will only be activated once an Asset/Account representing a Role gets checked out too, as the OneLogin_GRC_JIT_addon is the connector implementing JIT activation and elevation. - * In case the Account is assigned to multiple Roles via requesting multiple Asset/Accounts representing a OneLogin Role, the User should not check in any of the Roles before finishing all activities because checking one of these requests in will not only demote the requested Role, but also deactivate the Account inside OneLogin. - -### Configuration -It can be configured with two different approaches: -* **Accounts are created by OneLogin through its Generic REST Connector.** - * In this case the default Starling Connect connector for OneLogin is not used. Asset and Account objects are created by OneLogin, as well as the Entitlements and the Access Request Policies. - * Every Role which you want to make available for the privileged OneLogin account is mapped to an Asset/Account object in Safeguard, automatically by OneLogin. Corresponding Entitlements and Access Request policies are also created by OneLogin. - * OneLogin is automatically registering the TOTP for the privileged OneLogin account, as well as vaulting it in SPP (the TOTP seed is never exposed, users are technically forced to use the vaulted credential). -* **The base Account is created via the Discovery feature of the Starling Connect connector for OneLogin or in any other way, like via AD (in case the accounts are synchronized into OneLogin from AD).** - * In this case the Asset/Account objects representing the Role/User pairs must be created manually, or via 3rd party automation, so as the Entitlements and Access Request Policies. - * The TOTP seed for the base privileged OneLogin Account must be vaulted manually, or via 3rd party automation. - -#### Configuration: Accounts are created by OneLogin through its Generic REST Connector - - 1. Create the API Credential in OneLogin with Manage All permissions. This will be used as the service account for the Assets in Safeguard. - 2. Upload the custom platform script to SPP. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_5.png) - - - ![SafeguardCustomPlatform](assets/http_oneloginjit_6.png) - - - - 3. Configure the OneLogin REST Connector for Safeguard and let it do the heavy-lifting Safeguard: Assets, Accounts, Entitlements, Access Request Policies, etc. Search the **One Identity Safeguard (OneLogin Account Onboarding)** and **One Identity Safeguard (OneLogin-Virtual AssetAccounts for JIT elevation)** connectors in the OneLogin Application Catalog. - - - - -With this, the User is now able to raise Access Requests in Safeguard which enables the Account in OneLogin and assigns the requested Roles. - -#### Configuration: The base Account is created via the Discovery feature of the Starling Connect connector for OneLogin, or from Active Directory - -1. Onboard the OneLogin Accounts to SPP in the preferred way, for example using the out-of-box Starling Connect connector for OneLogin. This is going to be the main Account object holding the actual secrets of the privileged OneLogin Account. Feel free to manage these Accounts as needed. The Accounts may also originate from AD so that we can configure RDP Apps. In case the status and password of the OneLogin account is in sync with AD, then you can also manage the corresponding AD accounts in SPP. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_7.png) - - -2. Create the API Credential in OneLogin with Manage All permissions. This will be used as the service account for the Assets in Safeguard. - -3. Upload the custom platform script to SPP. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_8.png) - - - ![SafeguardCustomPlatform](assets/http_oneloginjit_9.png) - -4. Create an Asset for each OneLogin Role, or combination of Roles, that the User has permission to elevate into. The platform type is the OneLogin_GRC_JIT_addon. - - As the Roles look like in OneLogin: - - ![SafeguardCustomPlatform](assets/http_oneloginjit_10.png) - - - As the corresponding Assets look like in Safeguard: - - ![SafeguardCustomPlatform](assets/http_oneloginjit_11.png) - - -5. Create an Account on each of the these Assets with the same name as the original OneLogin Account. For example as shown on one of the Assets representing a OneLogin Role: - - ![SafeguardCustomPlatform](assets/http_oneloginjit_12.png) - - - Make sure that a dummy password is set on each of these Accounts otherwise these won't show up when raising an Access Request (note: the OneLogin_GRC_JIT_addon does not change the password of the Account, even if the Task is successfully completed). - - The Password Profile of these Accounts should do nothing with the password. No Check / No Change. - -6. Configure the corresponding Role name in the JIT configuration of these Accounts. For example as shown on one of the Assets representing a OneLogin Role: - - ![SafeguardCustomPlatform](assets/http_oneloginjit_13.png) - -7. If the base OneLogin Account is managed through the originating AD Account, then create an Entitlement for password or pession (RDP App) access with the Users' Linked Accounts. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_14.png) - - ![SafeguardCustomPlatform](assets/http_oneloginjit_15.png) - - ![SafeguardCustomPlatform](assets/http_oneloginjit_16.png) - -Don't forget creating the virtual asset to connect to: - - ![SafeguardCustomPlatform](assets/http_oneloginjit_17.png) - -Otherwise the Access Request Policy will be created in the per-user Entitlement together with the access to the virtual JIT Assets. - -8. Create an Entitlement per each User. This is required as at the time of writing this readme (in SPP v8.2) the Accounts of a Custom Platfom Asset can't be configured as Linked Accounts. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_18.png) - -9. Create a Dynamic Account Group for all the Role-specific Accounts of the User. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_19.png) - - ![SafeguardCustomPlatform](assets/http_oneloginjit_20.png) - - -10. Create a password Access Request Policy into the Entitlement. In the Scope of this Access Request Policy, add the Dynamic Account Group of the User. - - ![SafeguardCustomPlatform](assets/http_oneloginjit_21.png) - - ![SafeguardCustomPlatform](assets/http_oneloginjit_22.png) - - -With this, the User is now able to raise Access Requests in Safeguard which enables the Account in OneLogin and assigns the requested Roles. - -![SafeguardCustomPlatform](assets/http_oneloginjit_23.png) - - - -## Okta_WithDiscoveryAndGroupMembershipRestore -This script had been implemented before the JIT Elevation functionality was available in Safeguard. Hence the configuration is cumbersome and does not work as JIT is confgured. - -The script should be reworked a bit to reflect the out-of-box JIT configuration approach. diff --git a/SampleScripts/README.md b/SampleScripts/README.md deleted file mode 100644 index dfa2232..0000000 --- a/SampleScripts/README.md +++ /dev/null @@ -1,124 +0,0 @@ -To better understand these sample scripts, read the [documentation](../docs/README.md). - -These sample custom platform scripts show how to extend Safeguard for Privileged Passwords (SPP) with custom platforms that manage assets through the platform scripting engine over [SSH](SSH/), [Telnet (TN3270)](Telnet/), and [HTTP](HTTP/). Some target systems can be managed through more than one protocol, so start with the sample that is closest to your target workflow. - -For getting started, see the [overview](../docs/getting-started/overview.md) and -[development workflow](../docs/getting-started/development-workflow.md) guides. For -product-level details on custom platforms and asset onboarding, refer to the SPP -Administration Guide available from the [One Identity documentation site](https://docs.oneidentity.com/). - -## How to Use This Catalog - -- [`SSH/`](SSH/), [`HTTP/`](HTTP/), and [`Telnet/`](Telnet/) contain real tested samples. -- [`Templates/`](Templates/) contains illustrative examples that are **not** tested against live targets and are not intended to be deployed as-is. -- Files prefixed with `Pattern-` show recommended approaches for a specific integration pattern. -- Files prefixed with `Template` are minimal starters you can copy and fill in. -- Complexity ratings describe the expected customization effort: - - ⭐ **Beginner** — minimal starter or straightforward workflow - - ⭐⭐ **Intermediate** — multiple operations or moderate parsing/orchestration - - ⭐⭐⭐ **Advanced** — discovery, SSH key lifecycle, JIT elevation, dependent systems, file management, or other multi-step flows - -## SSH Samples (`SSH/`) - -Real tested SSH samples for Unix-like systems and appliances. - -| Sample | Complexity | Use case | -| --- | --- | --- | -| [`GenericLinux.json`](SSH/GenericLinux.json) | ⭐⭐ | Baseline Linux local account management over interactive SSH, including SSH host key discovery. | -| [`GenericLinuxWithAD.json`](SSH/GenericLinuxWithAD.json) | ⭐⭐ | Linux SSH account management where the functional account logs in with a domain-qualified identity. | -| [`GenericLinuxWithDiscovery.json`](SSH/GenericLinuxWithDiscovery.json) | ⭐⭐⭐ | Extends the baseline Linux SSH flow with local account discovery. | -| [`GenericLinuxWithSSHKeySupport.json`](SSH/GenericLinuxWithSSHKeySupport.json) | ⭐⭐⭐ | Adds authorized_keys discovery, validation, and rotation to Linux SSH account management. | -| [`LinuxApplicationTextConfig.json`](SSH/LinuxApplicationTextConfig.json) | ⭐⭐⭐ | Rotates an application password stored in a Linux text configuration file over SSH. | -| [`LinuxSshBatchModeExample.json`](SSH/LinuxSshBatchModeExample.json) | ⭐⭐ | Uses remote SSH commands in batch mode instead of an interactive shell for Linux password operations. | -| [`RestrictedAuthorizedKeyExample.json`](SSH/RestrictedAuthorizedKeyExample.json) | ⭐⭐⭐ | Authenticates the service account with a restricted authorized key and passwordless sudo for Linux password operations. | -| [`vCenterServerAppliance.json`](SSH/vCenterServerAppliance.json) | ⭐⭐⭐ | Manages VMware vCenter Server Appliance local root and SSO accounts, including account discovery and synchronized password handling. | - -> `LinuxApplicationTextConfig.json` is primarily a change-password example for file-based application credentials. - -## HTTP Samples (`HTTP/`) - -Real tested HTTP samples ranging from REST APIs to browser-form workflows. - -| Sample | Complexity | Use case | -| --- | --- | --- | -| [`CustomFacebook.json`](HTTP/CustomFacebook.json) | ⭐⭐⭐ | Browser-form HTTP example for Facebook-style credential validation and password change workflows. | -| [`CustomTwitter.json`](HTTP/CustomTwitter.json) | ⭐⭐⭐ | Browser-form HTTP example for Twitter-style login, challenge handling, lock detection, and password changes. | -| [`Forgerock_OpenAM.json`](HTTP/Forgerock_OpenAM.json) | ⭐⭐ | ForgeRock AM 7.5 REST sample for system validation and password rotation. | -| [`Okta_WithDiscoveryAndGroupMembershipRestore.json`](HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json) | ⭐⭐⭐ | Okta REST sample with account discovery plus group membership restore during enable and disable operations. | -| [`OneLogin_GRC_JIT_addon.json`](HTTP/OneLogin_GRC_JIT_addon.json) | ⭐⭐⭐ | OneLogin add-on for account activation and JIT role elevation in a PIAM/PIdP flow. | -| [`WordPressHttp.json`](HTTP/WordPressHttp.json) | ⭐⭐ | WordPress REST API sample using Basic Auth for system checks, credential validation, and password change. | - -## Telnet Samples (`Telnet/`) - -Real tested Telnet and TN3270 samples for network devices and mainframes. - -| Sample | Complexity | Use case | -| --- | --- | --- | -| [`GenericCiscoIosTelnet.json`](Telnet/GenericCiscoIosTelnet.json) | ⭐⭐⭐ | Cisco IOS Telnet sample for enable-mode validation and local or enable password rotation. | -| [`GenericRacfTn3270.json`](Telnet/GenericRacfTn3270.json) | ⭐⭐⭐ | IBM RACF TN3270 sample for mainframe logon validation and password changes. | - -## Templates (`Templates/`) - -The [`Templates/`](Templates/) folder contains illustrative scripts that help you design your own custom platform. These files are **not** tested against live targets. - -### Pattern templates (`Pattern-*.json`) - -Pattern templates show recommended approaches for specific scenarios. - -| Sample | Complexity | Use case | -| --- | --- | --- | -| [`Pattern-GenericHttpAccountDiscovery.json`](Templates/Pattern-GenericHttpAccountDiscovery.json) | ⭐⭐ | Illustrates paginated REST API account discovery with `WriteDiscoveredAccount`. | -| [`Pattern-GenericHttpJitElevation.json`](Templates/Pattern-GenericHttpJitElevation.json) | ⭐⭐⭐ | Illustrates idempotent JIT elevation over HTTP by adding and removing group membership. | -| [`Pattern-GenericLinuxDependentSystem.json`](Templates/Pattern-GenericLinuxDependentSystem.json) | ⭐⭐⭐ | Illustrates `UpdateDependentSystem` over SSH with a caller-provided dependency command. | -| [`Pattern-GenericLinuxFileManagement.json`](Templates/Pattern-GenericLinuxFileManagement.json) | ⭐⭐⭐ | Illustrates `CheckFile` and `ChangeFile` over SSH, including decode, deploy, and verify steps. | -| [`Pattern-GenericLinuxFull.json`](Templates/Pattern-GenericLinuxFull.json) | ⭐⭐⭐ | Illustrates a comprehensive Linux SSH platform spanning password, SSH key, discovery, and enable/disable operations. | -| [`Pattern-GenericLinuxServiceDiscovery.json`](Templates/Pattern-GenericLinuxServiceDiscovery.json) | ⭐⭐ | Illustrates Linux service discovery over SSH with `WriteDiscoveredService`. | -| [`Pattern-GenericRestApiBasicAuth.json`](Templates/Pattern-GenericRestApiBasicAuth.json) | ⭐⭐ | Illustrates REST API management using HTTP Basic auth for check, change, and discovery workflows. | -| [`Pattern-GenericRestApiBearerToken.json`](Templates/Pattern-GenericRestApiBearerToken.json) | ⭐⭐ | Illustrates REST API management using OAuth2 client credentials and bearer tokens. | -| [`Pattern-GenericRestApiKeyRotation.json`](Templates/Pattern-GenericRestApiKeyRotation.json) | ⭐⭐⭐ | Illustrates API key validation and rotation through a REST API lifecycle. | -| [`Pattern-WindowsSshBasic.json`](Templates/Pattern-WindowsSshBasic.json) | ⭐⭐ | Illustrates Windows password management over SSH with PowerShell and `net user`. | - -### Minimal starters (`Template*.json`) - -Minimal starters give you the smallest possible scaffold for a new platform. - -| Sample | Complexity | Use case | -| --- | --- | --- | -| [`TemplateHttpMinimal.json`](Templates/TemplateHttpMinimal.json) | ⭐ | Minimal HTTP starter that calls a health endpoint with a bearer token. | -| [`TemplateSshMinimal.json`](Templates/TemplateSshMinimal.json) | ⭐ | Minimal SSH starter that validates connectivity with a single echo command. | - -For a folder-focused listing, see [Templates/README.md](Templates/README.md). - -## Which Sample Should I Start With? - -| I need to… | Start here | -| --- | --- | -| Manage a Linux system over SSH | [`GenericLinux.json`](SSH/GenericLinux.json) — the baseline for interactive SSH workflows | -| Add account discovery to Linux | [`GenericLinuxWithDiscovery.json`](SSH/GenericLinuxWithDiscovery.json) — extends the baseline with `DiscoverAccounts` | -| Manage SSH keys on Linux | [`GenericLinuxWithSSHKeySupport.json`](SSH/GenericLinuxWithSSHKeySupport.json) — authorized_keys lifecycle | -| Use SSH in batch mode (no interactive shell) | [`LinuxSshBatchModeExample.json`](SSH/LinuxSshBatchModeExample.json) | -| Manage a REST API with Basic Auth | [`WordPressHttp.json`](HTTP/WordPressHttp.json) — simple REST check/change pattern | -| Manage a REST API with tokens | [`Forgerock_OpenAM.json`](HTTP/Forgerock_OpenAM.json) — token-based REST workflow | -| Discover accounts via REST API | [`Okta_WithDiscoveryAndGroupMembershipRestore.json`](HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json) | -| Implement JIT privilege elevation | [`OneLogin_GRC_JIT_addon.json`](HTTP/OneLogin_GRC_JIT_addon.json) | -| Handle browser-form login flows | [`CustomFacebook.json`](HTTP/CustomFacebook.json) or [`CustomTwitter.json`](HTTP/CustomTwitter.json) | -| Manage a network device over Telnet | [`GenericCiscoIosTelnet.json`](Telnet/GenericCiscoIosTelnet.json) | -| Manage a mainframe (TN3270) | [`GenericRacfTn3270.json`](Telnet/GenericRacfTn3270.json) | -| Start from scratch (SSH) | [`TemplateSshMinimal.json`](Templates/TemplateSshMinimal.json) — smallest possible scaffold | -| Start from scratch (HTTP) | [`TemplateHttpMinimal.json`](Templates/TemplateHttpMinimal.json) — smallest possible scaffold | - -## Samples by Feature (Advanced) - -Features that only a few samples demonstrate — useful when you need a specific capability: - -| Feature | Samples | -| --- | --- | -| Account discovery (`DiscoverAccounts`) | [`GenericLinuxWithDiscovery.json`](SSH/GenericLinuxWithDiscovery.json), [`vCenterServerAppliance.json`](SSH/vCenterServerAppliance.json), [`Okta_WithDiscoveryAndGroupMembershipRestore.json`](HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json) | -| SSH key rotation (`CheckSshKey` / `ChangeSshKey`) | [`GenericLinuxWithSSHKeySupport.json`](SSH/GenericLinuxWithSSHKeySupport.json) | -| SSH key discovery (`DiscoverAuthorizedKeys`) | [`GenericLinuxWithSSHKeySupport.json`](SSH/GenericLinuxWithSSHKeySupport.json) | -| Enable / Disable account | [`Okta_WithDiscoveryAndGroupMembershipRestore.json`](HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json), [`OneLogin_GRC_JIT_addon.json`](HTTP/OneLogin_GRC_JIT_addon.json) | -| JIT elevation (`ElevateAccount` / `DemoteAccount`) | [`OneLogin_GRC_JIT_addon.json`](HTTP/OneLogin_GRC_JIT_addon.json) | -| Dependent systems (`UpdateDependentSystem`) | Pattern: [`Pattern-GenericLinuxDependentSystem.json`](Templates/Pattern-GenericLinuxDependentSystem.json) | -| File management (`CheckFile` / `ChangeFile`) | Pattern: [`Pattern-GenericLinuxFileManagement.json`](Templates/Pattern-GenericLinuxFileManagement.json) | -| Service discovery (`DiscoverServices`) | Pattern: [`Pattern-GenericLinuxServiceDiscovery.json`](Templates/Pattern-GenericLinuxServiceDiscovery.json) | -| API key rotation (`CheckApiKey` / `ChangeApiKey`) | Pattern: [`Pattern-GenericRestApiKeyRotation.json`](Templates/Pattern-GenericRestApiKeyRotation.json) | diff --git a/SampleScripts/SSH/README.md b/SampleScripts/SSH/README.md deleted file mode 100644 index 18538e4..0000000 --- a/SampleScripts/SSH/README.md +++ /dev/null @@ -1,5 +0,0 @@ -[← Sample Scripts](../README.md) - -The Generic Linux script in this directory goes along with a -document -that describes how to write an SSH custom platform script that Safeguard understands. diff --git a/docs/getting-started/overview.md b/docs/concepts/architecture.md similarity index 100% rename from docs/getting-started/overview.md rename to docs/concepts/architecture.md diff --git a/docs/guides/feature-flags.md b/docs/concepts/feature-flags.md similarity index 100% rename from docs/guides/feature-flags.md rename to docs/concepts/feature-flags.md diff --git a/docs/guides/.gitkeep b/docs/guides/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/getting-started/development-workflow.md b/docs/guides/development-workflow.md similarity index 100% rename from docs/getting-started/development-workflow.md rename to docs/guides/development-workflow.md diff --git a/docs/getting-started/testing-and-debugging.md b/docs/guides/testing-and-debugging.md similarity index 100% rename from docs/getting-started/testing-and-debugging.md rename to docs/guides/testing-and-debugging.md diff --git a/docs/compatibility.md b/docs/reference/compatibility.md similarity index 100% rename from docs/compatibility.md rename to docs/reference/compatibility.md diff --git a/docs/getting-started/your-first-form-script.md b/docs/tutorials/your-first-form-script.md similarity index 100% rename from docs/getting-started/your-first-form-script.md rename to docs/tutorials/your-first-form-script.md diff --git a/docs/getting-started/your-first-http-script.md b/docs/tutorials/your-first-http-script.md similarity index 100% rename from docs/getting-started/your-first-http-script.md rename to docs/tutorials/your-first-http-script.md diff --git a/docs/getting-started/your-first-ssh-script.md b/docs/tutorials/your-first-ssh-script.md similarity index 100% rename from docs/getting-started/your-first-ssh-script.md rename to docs/tutorials/your-first-ssh-script.md diff --git a/SampleScripts/HTTP/CustomFacebook.json b/samples/http/facebook/CustomFacebook.json similarity index 100% rename from SampleScripts/HTTP/CustomFacebook.json rename to samples/http/facebook/CustomFacebook.json diff --git a/SampleScripts/HTTP/Forgerock_OpenAM.json b/samples/http/forgerock-openam/Forgerock_OpenAM.json similarity index 100% rename from SampleScripts/HTTP/Forgerock_OpenAM.json rename to samples/http/forgerock-openam/Forgerock_OpenAM.json diff --git a/SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json b/samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json similarity index 100% rename from SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json rename to samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json diff --git a/SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json b/samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json similarity index 100% rename from SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json rename to samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_1.mp4 b/samples/http/onelogin-jit/assets/http_oneloginjit_1.mp4 similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_1.mp4 rename to samples/http/onelogin-jit/assets/http_oneloginjit_1.mp4 diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_1.png b/samples/http/onelogin-jit/assets/http_oneloginjit_1.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_1.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_1.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_10.png b/samples/http/onelogin-jit/assets/http_oneloginjit_10.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_10.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_10.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_11.png b/samples/http/onelogin-jit/assets/http_oneloginjit_11.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_11.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_11.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_12.png b/samples/http/onelogin-jit/assets/http_oneloginjit_12.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_12.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_12.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_13.png b/samples/http/onelogin-jit/assets/http_oneloginjit_13.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_13.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_13.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_14.png b/samples/http/onelogin-jit/assets/http_oneloginjit_14.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_14.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_14.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_15.png b/samples/http/onelogin-jit/assets/http_oneloginjit_15.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_15.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_15.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_16.png b/samples/http/onelogin-jit/assets/http_oneloginjit_16.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_16.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_16.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_17.png b/samples/http/onelogin-jit/assets/http_oneloginjit_17.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_17.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_17.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_18.png b/samples/http/onelogin-jit/assets/http_oneloginjit_18.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_18.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_18.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_19.png b/samples/http/onelogin-jit/assets/http_oneloginjit_19.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_19.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_19.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_2.png b/samples/http/onelogin-jit/assets/http_oneloginjit_2.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_2.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_2.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_20.png b/samples/http/onelogin-jit/assets/http_oneloginjit_20.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_20.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_20.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_21.png b/samples/http/onelogin-jit/assets/http_oneloginjit_21.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_21.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_21.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_22.png b/samples/http/onelogin-jit/assets/http_oneloginjit_22.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_22.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_22.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_23.png b/samples/http/onelogin-jit/assets/http_oneloginjit_23.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_23.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_23.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_3.png b/samples/http/onelogin-jit/assets/http_oneloginjit_3.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_3.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_3.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_4.png b/samples/http/onelogin-jit/assets/http_oneloginjit_4.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_4.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_4.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_5.png b/samples/http/onelogin-jit/assets/http_oneloginjit_5.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_5.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_5.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_6.png b/samples/http/onelogin-jit/assets/http_oneloginjit_6.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_6.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_6.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_7.png b/samples/http/onelogin-jit/assets/http_oneloginjit_7.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_7.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_7.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_8.png b/samples/http/onelogin-jit/assets/http_oneloginjit_8.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_8.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_8.png diff --git a/SampleScripts/HTTP/assets/http_oneloginjit_9.png b/samples/http/onelogin-jit/assets/http_oneloginjit_9.png similarity index 100% rename from SampleScripts/HTTP/assets/http_oneloginjit_9.png rename to samples/http/onelogin-jit/assets/http_oneloginjit_9.png diff --git a/SampleScripts/HTTP/CustomTwitter.json b/samples/http/twitter/CustomTwitter.json similarity index 100% rename from SampleScripts/HTTP/CustomTwitter.json rename to samples/http/twitter/CustomTwitter.json diff --git a/SampleScripts/HTTP/WordPressHttp.json b/samples/http/wordpress/WordPressHttp.json similarity index 100% rename from SampleScripts/HTTP/WordPressHttp.json rename to samples/http/wordpress/WordPressHttp.json diff --git a/SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json b/samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json similarity index 100% rename from SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json rename to samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json diff --git a/SampleScripts/SSH/GenericLinuxWithAD.json b/samples/ssh/generic-linux-with-ad/GenericLinuxWithAD.json similarity index 100% rename from SampleScripts/SSH/GenericLinuxWithAD.json rename to samples/ssh/generic-linux-with-ad/GenericLinuxWithAD.json diff --git a/SampleScripts/SSH/GenericLinuxWithDiscovery.json b/samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json similarity index 100% rename from SampleScripts/SSH/GenericLinuxWithDiscovery.json rename to samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json diff --git a/SampleScripts/SSH/GenericLinux.json b/samples/ssh/generic-linux/GenericLinux.json similarity index 100% rename from SampleScripts/SSH/GenericLinux.json rename to samples/ssh/generic-linux/GenericLinux.json diff --git a/SampleScripts/SSH/LinuxApplicationTextConfig.json b/samples/ssh/linux-app-text-config/LinuxApplicationTextConfig.json similarity index 100% rename from SampleScripts/SSH/LinuxApplicationTextConfig.json rename to samples/ssh/linux-app-text-config/LinuxApplicationTextConfig.json diff --git a/SampleScripts/SSH/LinuxSshBatchModeExample.json b/samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json similarity index 100% rename from SampleScripts/SSH/LinuxSshBatchModeExample.json rename to samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json diff --git a/SampleScripts/SSH/RestrictedAuthorizedKeyExample.json b/samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json similarity index 100% rename from SampleScripts/SSH/RestrictedAuthorizedKeyExample.json rename to samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json diff --git a/SampleScripts/SSH/vCenterServerAppliance.json b/samples/ssh/vcenter-appliance/vCenterServerAppliance.json similarity index 100% rename from SampleScripts/SSH/vCenterServerAppliance.json rename to samples/ssh/vcenter-appliance/vCenterServerAppliance.json diff --git a/SampleScripts/Telnet/GenericCiscoIosTelnet.json b/samples/telnet/cisco-ios/GenericCiscoIosTelnet.json similarity index 100% rename from SampleScripts/Telnet/GenericCiscoIosTelnet.json rename to samples/telnet/cisco-ios/GenericCiscoIosTelnet.json diff --git a/SampleScripts/Telnet/GenericRacfTn3270.json b/samples/telnet/racf-tn3270/GenericRacfTn3270.json similarity index 100% rename from SampleScripts/Telnet/GenericRacfTn3270.json rename to samples/telnet/racf-tn3270/GenericRacfTn3270.json diff --git a/SampleScripts/Templates/Pattern-GenericHttpAccountDiscovery.json b/templates/Pattern-GenericHttpAccountDiscovery.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericHttpAccountDiscovery.json rename to templates/Pattern-GenericHttpAccountDiscovery.json diff --git a/SampleScripts/Templates/Pattern-GenericHttpJitElevation.json b/templates/Pattern-GenericHttpJitElevation.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericHttpJitElevation.json rename to templates/Pattern-GenericHttpJitElevation.json diff --git a/SampleScripts/Templates/Pattern-GenericLinuxDependentSystem.json b/templates/Pattern-GenericLinuxDependentSystem.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericLinuxDependentSystem.json rename to templates/Pattern-GenericLinuxDependentSystem.json diff --git a/SampleScripts/Templates/Pattern-GenericLinuxFileManagement.json b/templates/Pattern-GenericLinuxFileManagement.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericLinuxFileManagement.json rename to templates/Pattern-GenericLinuxFileManagement.json diff --git a/SampleScripts/Templates/Pattern-GenericLinuxFull.json b/templates/Pattern-GenericLinuxFull.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericLinuxFull.json rename to templates/Pattern-GenericLinuxFull.json diff --git a/SampleScripts/Templates/Pattern-GenericLinuxServiceDiscovery.json b/templates/Pattern-GenericLinuxServiceDiscovery.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericLinuxServiceDiscovery.json rename to templates/Pattern-GenericLinuxServiceDiscovery.json diff --git a/SampleScripts/Templates/Pattern-GenericRestApiBasicAuth.json b/templates/Pattern-GenericRestApiBasicAuth.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericRestApiBasicAuth.json rename to templates/Pattern-GenericRestApiBasicAuth.json diff --git a/SampleScripts/Templates/Pattern-GenericRestApiBearerToken.json b/templates/Pattern-GenericRestApiBearerToken.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericRestApiBearerToken.json rename to templates/Pattern-GenericRestApiBearerToken.json diff --git a/SampleScripts/Templates/Pattern-GenericRestApiKeyRotation.json b/templates/Pattern-GenericRestApiKeyRotation.json similarity index 100% rename from SampleScripts/Templates/Pattern-GenericRestApiKeyRotation.json rename to templates/Pattern-GenericRestApiKeyRotation.json diff --git a/SampleScripts/Templates/Pattern-WindowsSshBasic.json b/templates/Pattern-WindowsSshBasic.json similarity index 100% rename from SampleScripts/Templates/Pattern-WindowsSshBasic.json rename to templates/Pattern-WindowsSshBasic.json diff --git a/SampleScripts/Templates/README.md b/templates/README.md similarity index 100% rename from SampleScripts/Templates/README.md rename to templates/README.md diff --git a/SampleScripts/Templates/TemplateHttpMinimal.json b/templates/TemplateHttpMinimal.json similarity index 100% rename from SampleScripts/Templates/TemplateHttpMinimal.json rename to templates/TemplateHttpMinimal.json diff --git a/SampleScripts/Templates/TemplateSshMinimal.json b/templates/TemplateSshMinimal.json similarity index 100% rename from SampleScripts/Templates/TemplateSshMinimal.json rename to templates/TemplateSshMinimal.json From 831ac90fdd60bc560df3229f44697b549b325c11 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 18:33:22 -0600 Subject: [PATCH 02/13] Add navigation structure and new content documents - Rewrite root README.md as audience-driven front door - Rewrite docs/README.md as documentation index with reading paths - Add docs/quick-start/ with SSH and HTTP 5-minute guides - Add docs/concepts/ README, script-execution-model, and platform-lifecycle - Add docs/tutorials/ README and building-a-complete-platform capstone tutorial - Add docs/guides/ README with categorized guide index - Add docs/reference/ README with command lookup tables --- README.md | 91 ++++----- docs/README.md | 119 ++++++----- docs/concepts/README.md | 17 ++ docs/concepts/platform-lifecycle.md | 130 ++++++++++++ docs/concepts/script-execution-model.md | 89 +++++++++ docs/guides/README.md | 36 ++++ docs/quick-start/README.md | 10 + docs/quick-start/http-api-check.md | 68 +++++++ docs/quick-start/ssh-password-change.md | 62 ++++++ docs/reference/README.md | 34 ++++ docs/tutorials/README.md | 26 +++ .../tutorials/building-a-complete-platform.md | 185 ++++++++++++++++++ 12 files changed, 765 insertions(+), 102 deletions(-) create mode 100644 docs/concepts/README.md create mode 100644 docs/concepts/platform-lifecycle.md create mode 100644 docs/concepts/script-execution-model.md create mode 100644 docs/guides/README.md create mode 100644 docs/quick-start/README.md create mode 100644 docs/quick-start/http-api-check.md create mode 100644 docs/quick-start/ssh-password-change.md create mode 100644 docs/reference/README.md create mode 100644 docs/tutorials/README.md create mode 100644 docs/tutorials/building-a-complete-platform.md diff --git a/README.md b/README.md index a7e8fe3..8e6dae4 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,66 @@ # Safeguard Custom Platform Scripts -Build and adapt custom platform scripts for Safeguard when built-in platforms do not fit your target system. +Build custom platform scripts for [Safeguard for Privileged Passwords (SPP)](https://www.oneidentity.com/products/safeguard-for-privileged-passwords/) when built-in platforms don't cover your target system. -## What is this? +Custom platform scripts are JSON definitions that teach SPP how to connect to any target — Linux hosts, network appliances, REST APIs, web portals, cloud services — and manage credentials (passwords, SSH keys, API keys) through SSH, HTTP, or Telnet. -Safeguard custom platform scripts are JSON-based definitions that tell Safeguard for Privileged Passwords (SPP) how to connect to a target, navigate its interface, and perform credential operations such as password changes, key updates, and account validation. +## Where Do I Start? -This repository is for asset administrators and automation teams who need to manage passwords or SSH keys on operating systems, appliances, network devices, web applications, or vendor-specific workflows not covered by built-in platforms. It includes practical guidance and examples for both SSH- and HTTP-based integrations, plus historical Telnet-related content. +| I want to... | Go here | +| --- | --- | +| **Get something working in 5 minutes** | [Quick Start](docs/quick-start/) | +| **Understand how custom platforms work** | [Concepts](docs/concepts/) | +| **Learn step by step with a tutorial** | [Tutorials](docs/tutorials/) | +| **Look up a specific command or parameter** | [Reference](docs/reference/) | +| **Deploy a tested sample script** | [Samples](samples/) | +| **Start a new script from a template** | [Templates](templates/) | +| **Solve a specific problem** | [Guides](docs/guides/) | -## Quick Start +## Repository Layout -If you want the docs and samples locally while you work, clone the repository first: +``` +docs/ + quick-start/ 5-minute guides to get a working platform fast + concepts/ Architecture, execution model, feature flags + tutorials/ Step-by-step walkthroughs for building scripts + guides/ Task-focused how-to content (SSH patterns, HTTP patterns, etc.) + reference/ Commands, operations, parameters, variables +samples/ Production-tested scripts with companion documentation + ssh/ Linux, Unix, appliance samples + http/ REST API, OAuth2, form-based samples + telnet/ Cisco IOS, IBM RACF TN3270 samples +templates/ Pattern templates and minimal starters (not tested against live targets) +schema/ JSON Schema for IDE autocomplete +tools/ TestTool.ps1 for local validation +``` + +## Quick Start ```powershell +# Clone the repo git clone https://github.com/OneIdentity/SafeguardCustomPlatform.git cd SafeguardCustomPlatform -``` - -1. **Write.** Start with the closest template in `SampleScripts/Templates/`, then customize commands, prompts, parameters, and validation flow for your target. -2. **Upload.** Use `Import-SafeguardCustomPlatformScript` from `safeguard-ps` to upload the script to SPP. -3. **Test.** Validate against a safe test asset with `Test-SafeguardAssetAccountPassword -ExtendedLogging` before rolling into production. - -## Documentation -Start with [`docs/`](docs/) to find the right level of detail for your task: +# Pick a template and customize it +code templates/TemplateSshMinimal.json -- [`docs/getting-started/`](docs/getting-started/) - Tutorials and first-script walkthroughs for new custom platform authors. -- [`docs/reference/`](docs/reference/) - Script structure, supported operations, parameters, and command behavior. -- [`docs/guides/`](docs/guides/) - SSH patterns, HTTP patterns, regex guidance, and advanced implementation topics. -- [`docs/guides/regex-patterns.md`](docs/guides/regex-patterns.md) - Practical .NET regex patterns for prompts, parsing, and error detection. -- [`docs/guides/feature-flags.md`](docs/guides/feature-flags.md) - Understand which operations and capabilities your platform advertises. -- [`docs/guides/troubleshooting.md`](docs/guides/troubleshooting.md) - Common errors, diagnostic tips, and fixes. +# Upload to SPP +Import-SafeguardCustomPlatformScript -FilePath .\MyPlatform.json -## Sample Scripts - -Browse [`SampleScripts/`](SampleScripts/) for working examples you can study or adapt. Samples are organized by protocol so you can quickly focus on the right category: +# Test +Test-SafeguardAssetAccountPassword -AssetToUse "MyHost" -AccountToUse "admin" -ExtendedLogging +``` -- SSH -- HTTP -- Telnet +For detailed quick-start paths, see [docs/quick-start/](docs/quick-start/). ## Tools -Use [`tools/TestTool.ps1`](tools/TestTool.ps1) to test custom platform scripts locally before uploading them to SPP. - -## Telnet / Pattern Files - -Telnet pattern files have moved to [SafeguardAutomation](https://github.com/OneIdentity/SafeguardAutomation/tree/master/Terminal%20Pattern%20Files). +- [`tools/TestTool.ps1`](tools/TestTool.ps1) — Validate script JSON locally before uploading to SPP +- [`schema/custom-platform-script.schema.json`](schema/custom-platform-script.schema.json) — JSON Schema for IDE autocomplete (VS Code configured automatically) ## Contributing -Contributions are welcome, including new sample scripts, fixes, and documentation improvements. See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines, and feel free to share community-tested samples that others can adapt. +Contributions are welcome — new sample scripts, documentation improvements, and bug fixes. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. ## Support @@ -59,20 +69,3 @@ One Identity open source projects are supported through [GitHub issues](https:// ## License See [LICENSE](LICENSE). - -## Compatibility Matrix - -> Approximate only — check your SPP release notes for exact availability in your build. - -| SPP Version | Custom Platform Feature Added | -| --- | --- | -| 6.0 | Custom platforms introduced (SSH, Telnet) | -| 6.7 | HTTP/REST custom platforms added | -| 7.0 | `DiscoverAccounts` | -| 7.0 | `DiscoverServices` | -| 7.0 | `DiscoverSshHostKey` | -| 7.4 | `ExecuteCommand` (SSH batch mode) | -| 7.4 | `ExecuteDependentCommand` (dependent system workflows) | -| 7.5 | `ElevateAccount` / `DemoteAccount` | -| 7.5 | `EnableAccount` / `DisableAccount` | -| 7.6 | `CheckFile` / `ChangeFile` | diff --git a/docs/README.md b/docs/README.md index 4f3e72a..6d698ab 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,65 +1,78 @@ -# Safeguard Custom Platform Documentation +# Documentation -This documentation hub helps you learn, build, and maintain Safeguard Custom Platform scripts. Start here for onboarding material, detailed references, and task-focused guides. +Everything you need to learn, build, and maintain Safeguard custom platform scripts. -## Suggested Reading Order +## Reading Paths -1. [Overview](getting-started/overview.md) — Learn the architecture, execution model, and when a custom platform is the right fit. -2. [First Tutorial](getting-started/your-first-ssh-script.md) — Build your first SSH-based script step by step. -3. [Script Structure](reference/script-structure.md) — Understand the JSON layout, operations, and `Do` blocks used by every script. -4. [Operations Reference](reference/operations.md) — Review the supported operations available when implementing a platform. +Choose based on what you need right now: -## Getting Started +### 🚀 I want something working fast +→ [Quick Start](quick-start/) — 5-minute paths to a working platform. -| Document | Description | -| --- | --- | -| [getting-started/overview.md](getting-started/overview.md) | Architecture, script execution flow, and guidance on whether you need a custom platform. | -| [getting-started/your-first-ssh-script.md](getting-started/your-first-ssh-script.md) | Step-by-step tutorial for creating your first SSH custom platform script. | -| [getting-started/your-first-http-script.md](getting-started/your-first-http-script.md) | Step-by-step tutorial for creating your first HTTP custom platform script (REST API). | -| [getting-started/your-first-form-script.md](getting-started/your-first-form-script.md) | Step-by-step tutorial for managing passwords on web portals with HTML form submission. | -| [getting-started/development-workflow.md](getting-started/development-workflow.md) | End-to-end workflow from upload through testing and iteration. | -| [getting-started/testing-and-debugging.md](getting-started/testing-and-debugging.md) | Test tools, logs, and `extendedLogging` techniques for troubleshooting. | +### 🧠 I want to understand how this works +→ [Concepts](concepts/) — Architecture, execution model, feature flags, lifecycle. -## Reference +### 📖 I want to learn step by step +→ [Tutorials](tutorials/) — Build your first SSH, HTTP, or form-based script from scratch. -| Document | Description | -| --- | --- | -| [reference/script-structure.md](reference/script-structure.md) | JSON structure, top-level keys, operations, and `Do` blocks. | -| [reference/operations.md](reference/operations.md) | Reference for all supported operations. | -| [reference/reserved-parameters.md](reference/reserved-parameters.md) | Complete reference for reserved platform parameters. | -| [reference/custom-parameters.md](reference/custom-parameters.md) | How to define and use your own custom parameters. | -| [reference/variables.md](reference/variables.md) | Variable system reference for reading, setting, and reusing values. | -| [reference/commands/](reference/commands/) | Command reference organized by category. | -| [reference/imports.md](reference/imports.md) | Reusable SSH function libraries and import patterns. | -| [reference/status-messages.md](reference/status-messages.md) | Predefined status messages available to scripts. | - -## Guides - -| Document | Description | -| --- | --- | -| [guides/ssh-platforms.md](guides/ssh-platforms.md) | SSH design patterns and session integration guidance. | -| [guides/http-platforms.md](guides/http-platforms.md) | REST, OAuth2, and Bearer token implementation patterns. | -| [guides/account-discovery.md](guides/account-discovery.md) | Guidance for discovering accounts through custom platforms. | -| [guides/ssh-key-management.md](guides/ssh-key-management.md) | Patterns for checking, changing, and discovering SSH keys. | -| [guides/api-key-management.md](guides/api-key-management.md) | Approaches for API key rotation workflows. | -| [guides/file-management.md](guides/file-management.md) | Working with file-based credentials and related operations. | -| [guides/jit-elevation.md](guides/jit-elevation.md) | Implementing JIT elevation and demotion scenarios. | -| [guides/dependent-systems.md](guides/dependent-systems.md) | Updating dependent systems as part of platform workflows. | -| [guides/error-handling.md](guides/error-handling.md) | Try/Catch patterns for reliable error handling. | -| [guides/regex-patterns.md](guides/regex-patterns.md) | Practical .NET regex patterns for prompts, parsing, and error detection. | -| [guides/feature-flags.md](guides/feature-flags.md) | How script content enables and shapes platform capabilities. | -| [guides/troubleshooting.md](guides/troubleshooting.md) | Common errors, diagnostics, and recommended fixes. | - -## General - -| Document | Description | -| --- | --- | -| [compatibility.md](compatibility.md) | Compatibility matrix for documented SPP custom platform capabilities and versions. | +### 🔧 I need to solve a specific problem +→ [Guides](guides/) — Task-focused how-to content: SSH patterns, HTTP patterns, discovery, error handling, testing. + +### 📋 I need to look up a detail +→ [Reference](reference/) — Commands, operations, parameters, variables, imports. + +--- + +## Full Table of Contents + +### Quick Start +- [SSH Password Change in 5 Minutes](quick-start/ssh-password-change.md) +- [HTTP API Check in 5 Minutes](quick-start/http-api-check.md) + +### Concepts +- [Architecture](concepts/architecture.md) — What custom platforms are and when you need one +- [Script Execution Model](concepts/script-execution-model.md) — How SPP loads and runs scripts +- [Feature Flags](concepts/feature-flags.md) — Automatic capability derivation from script content +- [Platform Lifecycle](concepts/platform-lifecycle.md) — From authoring to production and updates + +### Tutorials +- [Your First SSH Script](tutorials/your-first-ssh-script.md) +- [Your First HTTP Script](tutorials/your-first-http-script.md) +- [Your First Form Script](tutorials/your-first-form-script.md) +- [Building a Complete Platform](tutorials/building-a-complete-platform.md) + +### Guides +- [Development Workflow](guides/development-workflow.md) +- [Testing and Debugging](guides/testing-and-debugging.md) +- [SSH Platforms](guides/ssh-platforms.md) +- [HTTP Platforms](guides/http-platforms.md) +- [Account Discovery](guides/account-discovery.md) +- [SSH Key Management](guides/ssh-key-management.md) +- [API Key Management](guides/api-key-management.md) +- [File Management](guides/file-management.md) +- [JIT Elevation](guides/jit-elevation.md) +- [Dependent Systems](guides/dependent-systems.md) +- [Error Handling](guides/error-handling.md) +- [Regex Patterns](guides/regex-patterns.md) +- [Troubleshooting](guides/troubleshooting.md) + +### Reference +- [Script Structure](reference/script-structure.md) +- [Operations](reference/operations.md) (19 operations) +- [Reserved Parameters](reference/reserved-parameters.md) (72 parameters) +- [Custom Parameters](reference/custom-parameters.md) +- [Variables](reference/variables.md) +- [Commands](reference/commands/) (60+ commands) +- [Imports](reference/imports.md) +- [Status Messages](reference/status-messages.md) +- [Compatibility Matrix](reference/compatibility.md) ## Additional Resources | Resource | Description | | --- | --- | -| [SampleScripts](../SampleScripts/) | Working examples you can use as references when building scripts. | -| [TestTool.ps1](../tools/TestTool.ps1) | Local test tool for validating and iterating on platform scripts. | -| [One Identity Support](https://support.oneidentity.com/) | General Safeguard product documentation, downloads, and support resources. | +| [Samples](../samples/) | Production-tested scripts with companion documentation | +| [Templates](../templates/) | Pattern templates and minimal starters | +| [TestTool.ps1](../tools/TestTool.ps1) | Local script validation tool | +| [JSON Schema](../schema/custom-platform-script.schema.json) | IDE autocomplete schema | +| [One Identity Support](https://support.oneidentity.com/) | Official product support | diff --git a/docs/concepts/README.md b/docs/concepts/README.md new file mode 100644 index 0000000..fc80b60 --- /dev/null +++ b/docs/concepts/README.md @@ -0,0 +1,17 @@ +# Concepts + +Understand how Safeguard custom platforms work before you start building. + +| Document | Description | +| --- | --- | +| [Architecture](architecture.md) | What custom platforms are, the execution model, and when you need one. | +| [Script Execution Model](script-execution-model.md) | How SPP loads, validates, expands, and runs your script at task time. | +| [Feature Flags](feature-flags.md) | How SPP automatically derives platform capabilities from your script content. | +| [Platform Lifecycle](platform-lifecycle.md) | The full lifecycle: authoring → upload → testing → production → updates. | + +## Suggested Reading Order + +1. Start with [Architecture](architecture.md) if you're completely new. +2. Read [Feature Flags](feature-flags.md) to understand why your script *is* your configuration. +3. Read [Script Execution Model](script-execution-model.md) when you want to understand what happens at runtime. +4. Read [Platform Lifecycle](platform-lifecycle.md) when you're ready to deploy to production. diff --git a/docs/concepts/platform-lifecycle.md b/docs/concepts/platform-lifecycle.md new file mode 100644 index 0000000..e56d028 --- /dev/null +++ b/docs/concepts/platform-lifecycle.md @@ -0,0 +1,130 @@ +# Platform Lifecycle + +This document covers the full lifecycle of a custom platform — from initial development through production deployment and ongoing maintenance. + +## Phases + +``` +Author → Upload → Test → Deploy → Monitor → Update +``` + +## 1. Author + +Write your script as a JSON file on your workstation. Use an IDE with the JSON schema for autocomplete: + +- Schema: [`schema/custom-platform-script.schema.json`](../../schema/custom-platform-script.schema.json) +- VS Code setup: the `.vscode/` directory in this repo configures schema association automatically. + +Start from a [template](../../templates/) or an existing [sample](../../samples/) that matches your target system. + +**Best practices during authoring:** +- Start small — get `CheckSystem` working first, then add operations incrementally. +- Use the [development workflow](../guides/development-workflow.md) guide for the upload-test-iterate cycle. +- Test each operation independently before combining them. + +## 2. Upload + +Upload your script to SPP using either method: + +**PowerShell (recommended for development):** +```powershell +Import-SafeguardCustomPlatformScript -FilePath .\MyPlatform.json +``` + +**Web UI:** +1. Navigate to **Asset Management > Connect and Platforms > Custom Platforms** +2. Click **Add** +3. Browse to your JSON file and upload + +SPP validates the script immediately. If validation fails, you get an error message describing the issue. Fix the script and re-upload. + +## 3. Test + +Testing happens in two stages: + +### Local Validation +Use the [TestTool](../../tools/TestTool.ps1) to validate JSON structure before uploading: +```powershell +.\tools\TestTool.ps1 -ScriptFile .\MyPlatform.json +``` + +### Live Testing +After upload, test against a real (non-production) target: + +1. Create a test asset using your custom platform +2. Configure valid credentials +3. Run individual operations: + +```powershell +# Test connectivity +Test-SafeguardAssetConnection -AssetToUse "TestHost" -ExtendedLogging + +# Test password check +Test-SafeguardAssetAccountPassword -AssetToUse "TestHost" -AccountToUse "testuser" -ExtendedLogging + +# Test password change (use a disposable test account!) +Invoke-SafeguardAssetAccountPasswordChange -AssetToUse "TestHost" -AccountToUse "testuser" +``` + +The `-ExtendedLogging` flag captures the full execution trace in the task log, which is essential for debugging. + +See [Testing and Debugging](../guides/testing-and-debugging.md) for detailed guidance. + +## 4. Deploy + +Once testing passes: + +1. Create production assets using the custom platform +2. Assign real service accounts and managed accounts +3. Configure check and change schedules +4. Set up profiles and access policies as needed + +**Deployment checklist:** +- [ ] All operations tested successfully with ExtendedLogging +- [ ] Error paths tested (wrong password, unreachable host, locked account) +- [ ] Service account has appropriate privileges on the target +- [ ] Network connectivity confirmed from the SPP appliance to the target +- [ ] Schedules configured appropriately (not too aggressive) + +## 5. Monitor + +After deployment, monitor platform health through: + +- **Task logs** — Check for failed tasks in the SPP Activity Center +- **Password check results** — Confirm scheduled checks pass consistently +- **Account discovery** — If enabled, verify discovered accounts appear correctly + +## 6. Update + +To update a platform script: + +1. Download the current version: use the **Download** button in Custom Platforms or the API +2. Make your changes locally +3. Test the changes against a test asset +4. Upload the updated script — SPP replaces the old version + +**Important:** Updating a script does NOT change parameter defaults on existing assets. If you add a new custom parameter with a default value, existing assets won't pick up that default automatically — you need to update them individually or recreate them. + +## Version Management + +SPP does not version custom platform scripts internally. Best practices: + +- Keep your scripts in version control (like this repository) +- Use meaningful commit messages for changes +- Tag releases if you distribute scripts to multiple SPP instances +- Document breaking changes (parameter renames, operation removals) clearly + +## Deprecation + +To deprecate a custom platform: + +1. Ensure no assets are actively using it (or migrate them to a replacement) +2. Delete the custom platform from **Asset Management > Custom Platforms** + +**Warning:** Deleting a custom platform that is assigned to assets will reassign those assets to the "Other" platform type, which halts all credential management operations. + +## Related + +- [Architecture](architecture.md) — how custom platforms fit into SPP +- [Development Workflow](../guides/development-workflow.md) — the upload-test-iterate cycle +- [Testing and Debugging](../guides/testing-and-debugging.md) — detailed testing guidance diff --git a/docs/concepts/script-execution-model.md b/docs/concepts/script-execution-model.md new file mode 100644 index 0000000..dc3bd72 --- /dev/null +++ b/docs/concepts/script-execution-model.md @@ -0,0 +1,89 @@ +# Script Execution Model + +This document explains what happens when SPP runs a custom platform script — from the moment you upload a JSON file to the moment a task executes against a target system. + +## Upload and Validation + +When you upload a script (via the web UI or `Import-SafeguardCustomPlatformScript`), SPP performs these steps in order: + +1. **JSON parse** — The file must be valid JSON. Syntax errors are rejected immediately. +2. **Structure validation** — SPP checks for required top-level keys (`Id`, `BackEnd`) and verifies that operations contain `Parameters` and `Do` arrays. +3. **Parameter type checking** — Each parameter is validated against known types (string, boolean, integer, password/secret). Reserved parameter names are matched against their expected types. +4. **Import expansion** — If the script declares `Imports`, SPP locates the referenced function libraries and merges their function definitions into the script. The expanded script is what gets stored. +5. **Feature flag derivation** — SPP scans the operations present in the script and sets capability flags automatically. See [Feature Flags](feature-flags.md). +6. **Storage** — The validated, expanded script is stored in the SPP database and the platform is ready for use. + +## Task Execution + +When SPP needs to perform an operation (e.g., a scheduled password check), this is the execution flow: + +### 1. Operation Selection + +SPP determines which operation to call based on the task type: +- A password check task invokes `CheckPassword` +- A password change task invokes `ChangePassword` +- An account discovery task invokes `DiscoverAccounts` +- And so on for all 19 operations + +### 2. Parameter Population + +SPP auto-populates reserved parameters from the asset and account configuration: +- `Address` ← asset network address +- `Port` ← asset connection port +- `FuncUserName` / `FuncPassword` ← service account credentials +- `AccountUserName` / `AccountPassword` ← managed account credentials +- `NewPassword` ← the SPP-generated replacement password (for change operations) + +Custom parameters retain their configured default values unless overridden at the asset level. + +### 3. Script Engine Execution + +The Hercules script engine processes the operation's `Do` array sequentially: + +1. Each command in the `Do` array is executed in order. +2. Variable interpolation (`%VariableName%`) is resolved at execution time. +3. Commands may set variables, make connections, send data, or branch execution. +4. Flow control commands (`Condition`, `Switch`, `For`, `ForEach`) alter the execution path. +5. Functions can be called and may return values. +6. Error handling (`Try`/`Catch`) provides recovery paths. + +### 4. Result Determination + +The operation result is determined by: +- **Success** — The `Do` block completes without an unhandled error. +- **Failure** — A `Throw` command executes without a surrounding `Try`/`Catch`, or a connection fails, or an unrecoverable error occurs. + +For discovery operations, success means the script called `WriteDiscoveredAccount` (or `WriteDiscoveredService`, etc.) one or more times before completing. + +## Connection Lifecycle + +For SSH operations, the connection lifecycle is: +1. `Connect` opens the SSH session (interactive or batch mode) +2. Commands interact with the remote system +3. `Disconnect` closes the session +4. If the script ends without `Disconnect`, the engine cleans up automatically + +For HTTP operations, there is no persistent connection — each `Request` is independent (though cookies and session state persist across requests within a single operation execution). + +## Import Libraries + +When a script declares `Imports`, the referenced function libraries are expanded into the script at upload time. At runtime, these imported functions are available for the script to call just like locally-defined functions. + +Import libraries provide reusable patterns for common tasks (e.g., SSH login sequences, privilege escalation, output parsing). See [Imports Reference](../reference/imports.md) for the full catalog. + +## Error Propagation + +Errors propagate up the call stack: +1. A command fails or `Throw` is called +2. If inside a `Try` block, execution jumps to the `Catch` block +3. If not caught, the error propagates to the calling function +4. If uncaught at the top level, the operation fails + +The task log captures all errors and, with `ExtendedLogging` enabled, captures the full execution trace. + +## Related + +- [Architecture](architecture.md) — high-level overview +- [Feature Flags](feature-flags.md) — how capabilities are derived +- [Operations Reference](../reference/operations.md) — all 19 operations +- [Testing and Debugging](../guides/testing-and-debugging.md) — how to diagnose execution issues diff --git a/docs/guides/README.md b/docs/guides/README.md new file mode 100644 index 0000000..713fec1 --- /dev/null +++ b/docs/guides/README.md @@ -0,0 +1,36 @@ +# Guides + +Task-focused how-to documentation for building and maintaining custom platforms. Each guide covers a specific topic in depth. + +## Development + +| Guide | Description | +| --- | --- | +| [Development Workflow](development-workflow.md) | End-to-end workflow from authoring through upload, testing, and iteration. | +| [Testing and Debugging](testing-and-debugging.md) | Test tools, task logs, `ExtendedLogging`, and diagnostic techniques. | +| [Error Handling](error-handling.md) | Try/Catch patterns for reliable error recovery in scripts. | +| [Regex Patterns](regex-patterns.md) | Practical .NET regex patterns for prompts, parsing, and error detection. | +| [Troubleshooting](troubleshooting.md) | Common errors, diagnostics, and recommended fixes. | + +## SSH Platforms + +| Guide | Description | +| --- | --- | +| [SSH Platforms](ssh-platforms.md) | SSH design patterns: interactive vs. batch, login flows, prompt detection. | +| [SSH Key Management](ssh-key-management.md) | Checking, changing, and discovering SSH authorized keys. | + +## HTTP Platforms + +| Guide | Description | +| --- | --- | +| [HTTP Platforms](http-platforms.md) | REST, OAuth2, Bearer tokens, forms, and cookie-based workflows. | +| [API Key Management](api-key-management.md) | API key validation and rotation patterns. | + +## Advanced Features + +| Guide | Description | +| --- | --- | +| [Account Discovery](account-discovery.md) | Implementing `DiscoverAccounts` and `DiscoverServices` operations. | +| [File Management](file-management.md) | Working with file-based credentials (`CheckFile`/`ChangeFile`). | +| [JIT Elevation](jit-elevation.md) | Implementing just-in-time privilege elevation and demotion. | +| [Dependent Systems](dependent-systems.md) | Updating dependent systems as part of credential rotation workflows. | diff --git a/docs/quick-start/README.md b/docs/quick-start/README.md new file mode 100644 index 0000000..38adf28 --- /dev/null +++ b/docs/quick-start/README.md @@ -0,0 +1,10 @@ +# Quick Start + +Pick the path that matches your target system and get a working custom platform in under 5 minutes. + +| I need to manage... | Guide | +| --- | --- | +| A Linux/Unix system over SSH | [SSH Password Change](ssh-password-change.md) | +| A REST API over HTTP | [HTTP API Check](http-api-check.md) | + +These guides skip the theory and focus on getting a result. For deeper understanding, see [Concepts](../concepts/) or [Tutorials](../tutorials/). diff --git a/docs/quick-start/http-api-check.md b/docs/quick-start/http-api-check.md new file mode 100644 index 0000000..19d1b77 --- /dev/null +++ b/docs/quick-start/http-api-check.md @@ -0,0 +1,68 @@ +# Quick Start: HTTP API Check + +Get a working custom platform that validates connectivity to a REST API over HTTP in under 5 minutes. + +## What You'll Get + +A minimal platform script that sends an HTTP request to an API endpoint to verify that the system is reachable and credentials are valid. + +## Steps + +### 1. Copy the Minimal Template + +Download or copy [`TemplateHttpMinimal.json`](../../templates/TemplateHttpMinimal.json) and rename it (e.g., `MyApiCheck.json`). + +### 2. Customize the Endpoint + +Open the file and update the `CheckSystem` operation to hit your API's health or authentication endpoint. For example, if your API uses Basic Auth: + +```json +"CheckSystem": { + "Parameters": [ + { "Address": "" }, + { "FuncUserName": "" }, + { "FuncPassword": "" } + ], + "Do": [ + { "BaseAddress": "https://%Address%" }, + { "NewHttpRequest": { "Name": "req" } }, + { "HttpAuth": { "Type": "Basic", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "Request": "req" } }, + { "Request": { "Method": "GET", "Url": "/api/v1/health", "Request": "req" } }, + { + "Condition": { + "If": "Response.StatusCode != 200", + "Then": { "Do": [{ "Throw": { "Message": "System check failed: HTTP %Response.StatusCode%" } }] } + } + } + ] +} +``` + +Replace `/api/v1/health` with your target's actual endpoint. + +### 3. Upload to SPP + +```powershell +Import-SafeguardCustomPlatformScript -FilePath .\MyApiCheck.json +``` + +### 4. Create an Asset + +1. In the SPP web UI, go to **Asset Management > Assets > Add** +2. Set the platform to your new custom platform +3. Set the network address to the API hostname +4. Assign a service account with API credentials (username/password or token) + +### 5. Test + +```powershell +Test-SafeguardAssetConnection -AssetToUse "MyApiServer" -ExtendedLogging +``` + +If it reports success, SPP can reach your API. + +## Next Steps + +- Add `CheckPassword` and `ChangePassword` — see [Your First HTTP Script](../tutorials/your-first-http-script.md) +- Learn about HTTP authentication patterns — see [HTTP Platforms Guide](../guides/http-platforms.md) +- Study a production-ready sample — see [WordPress HTTP](../../samples/http/wordpress/) diff --git a/docs/quick-start/ssh-password-change.md b/docs/quick-start/ssh-password-change.md new file mode 100644 index 0000000..a707153 --- /dev/null +++ b/docs/quick-start/ssh-password-change.md @@ -0,0 +1,62 @@ +# Quick Start: SSH Password Check + +Get a working custom platform that validates a Linux password over SSH in under 5 minutes. + +## What You'll Get + +A minimal platform script that connects to a Linux host and verifies a managed account password using `CheckPassword`. + +## Steps + +### 1. Copy the Minimal Template + +Download or copy [`TemplateSshMinimal.json`](../../templates/TemplateSshMinimal.json) and rename it (e.g., `MyLinuxCheck.json`). + +### 2. Add CheckPassword + +Open the file and add this `CheckPassword` operation after the existing `CheckSystem`: + +```json +"CheckPassword": { + "Parameters": [ + { "Address": "" }, + { "Port": "" }, + { "AccountUserName": "" }, + { "AccountPassword": "" } + ], + "Do": [ + { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%AccountUserName%", "Password": "%AccountPassword%", "RequestTerminal": true } }, + { "Disconnect": {} } + ] +} +``` + +This connects with the managed account's credentials. If the connection succeeds, the password is valid. + +### 3. Upload to SPP + +```powershell +Import-SafeguardCustomPlatformScript -FilePath .\MyLinuxCheck.json +``` + +### 4. Create an Asset + +1. In the SPP web UI, go to **Asset Management > Assets > Add** +2. Set the platform to your new custom platform +3. Configure the network address (IP or hostname) and port (default 22) +4. Assign a service account (the account SPP uses to connect for `CheckSystem`) +5. Add a managed account (the account whose password you want to validate) + +### 5. Test + +```powershell +Test-SafeguardAssetAccountPassword -AssetToUse "MyLinuxHost" -AccountToUse "root" -ExtendedLogging +``` + +If it reports success, you have a working custom platform. + +## Next Steps + +- Add `ChangePassword` — see [Your First SSH Script](../tutorials/your-first-ssh-script.md) +- Understand the full script structure — see [Script Structure](../reference/script-structure.md) +- Study a production-ready sample — see [Generic Linux](../../samples/ssh/generic-linux/) diff --git a/docs/reference/README.md b/docs/reference/README.md new file mode 100644 index 0000000..3b0d018 --- /dev/null +++ b/docs/reference/README.md @@ -0,0 +1,34 @@ +# Reference + +Detailed reference documentation for every aspect of custom platform scripts. Use this section when you need to look up a specific command, parameter, operation, or behavior. + +## Core References + +| Document | Description | +| --- | --- | +| [Script Structure](script-structure.md) | JSON layout, top-level keys, operations, `Do` blocks, and functions. | +| [Operations](operations.md) | All 19 operations with credential contexts and feature flags. | +| [Reserved Parameters](reserved-parameters.md) | Parameters SPP auto-populates (72 parameters documented). | +| [Custom Parameters](custom-parameters.md) | Defining your own parameters with types, defaults, and UI behavior. | +| [Variables](variables.md) | Variable interpolation, scope rules, and the expression engine. | +| [Status Messages](status-messages.md) | Predefined status messages available to scripts. | +| [Imports](imports.md) | Reusable SSH function libraries and the import catalog. | +| [Compatibility](compatibility.md) | Which features are available in which SPP versions. | + +## Command Reference + +The [Commands](commands/) section documents every command available in `Do` blocks, organized by category: + +| Category | Commands | Page | +| --- | --- | --- | +| HTTP | `Request`, `NewHttpRequest`, `BaseAddress`, `Headers`, `HttpAuth`, cookies, forms, JSON, encoding | [commands/](commands/index.md) | +| SSH/Telnet | `Connect`, `Disconnect`, `Send`, `Receive`, `ExecuteCommand`, `DiscoverSshHostKey` | [commands/](commands/index.md) | +| Flow Control | `Condition`, `Switch`, `For`, `ForEach` | [commands/](commands/index.md) | +| Functions | `Function`, `Return`, `Break`, `Eval` | [commands/](commands/index.md) | +| Error Handling | `Try`, `Throw` | [commands/](commands/index.md) | +| Utilities | `SetItem`, `Declare`, `Split`, `Comment` | [commands/](commands/index.md) | +| Output | `WriteDiscoveredAccount`, `WriteDiscoveredService`, `WriteDiscoveredSshKey`, `WriteResponseObject` | [commands/](commands/index.md) | +| Logging | `Log`, `Status`, `Wait` | [commands/](commands/index.md) | +| Dependencies | `ExecuteDependentCommand` | [commands/](commands/index.md) | + +See [commands/index.md](commands/index.md) for the full navigable reference. diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md new file mode 100644 index 0000000..0a89360 --- /dev/null +++ b/docs/tutorials/README.md @@ -0,0 +1,26 @@ +# Tutorials + +Step-by-step walkthroughs that teach you how to build custom platform scripts from scratch. + +| Tutorial | Description | +| --- | --- | +| [Your First SSH Script](your-first-ssh-script.md) | Build a minimal SSH platform with `CheckSystem` and `CheckPassword`. | +| [Your First HTTP Script](your-first-http-script.md) | Build a minimal REST API platform with Basic Auth. | +| [Your First Form Script](your-first-form-script.md) | Manage passwords on a web portal using HTML form submission. | +| [Building a Complete Platform](building-a-complete-platform.md) | Go from a minimal script to a full-featured platform with multiple operations. | + +## Before You Start + +Make sure you have: +- A target system to test against (VM, container, or dev instance) +- An SPP appliance with the `safeguard-ps` PowerShell module +- Basic familiarity with JSON + +If you just want something working fast without the learning, try [Quick Start](../quick-start/) instead. + +## After Tutorials + +Once you've completed a tutorial, explore: +- [Guides](../guides/) — task-focused how-to content for specific features +- [Reference](../reference/) — look up any command, parameter, or operation in detail +- [Samples](../../samples/) — production-tested scripts you can study or deploy diff --git a/docs/tutorials/building-a-complete-platform.md b/docs/tutorials/building-a-complete-platform.md new file mode 100644 index 0000000..a132bb5 --- /dev/null +++ b/docs/tutorials/building-a-complete-platform.md @@ -0,0 +1,185 @@ +# Building a Complete Platform + +This tutorial takes you from a minimal custom platform script to a full-featured one with multiple operations. By the end, you'll have a platform that can check connectivity, validate passwords, change passwords, and discover accounts — all over SSH. + +## Prerequisites + +- Completed [Your First SSH Script](your-first-ssh-script.md) (or equivalent knowledge) +- A Linux target with SSH access and at least two user accounts +- SPP with `safeguard-ps` installed + +## What You'll Build + +A complete Linux SSH platform with these operations: + +| Operation | Purpose | +| --- | --- | +| `CheckSystem` | Verify the service account can connect | +| `CheckPassword` | Validate a managed account's password | +| `ChangePassword` | Rotate a managed account's password | +| `DiscoverAccounts` | Find local accounts on the system | + +## Step 1: Start with CheckSystem and CheckPassword + +If you followed the first SSH tutorial, you already have a script with these two operations. If not, start with: + +```json +{ + "Id": "MyCompletePlatform", + "BackEnd": "Scriptable", + "CheckSystem": { + "Parameters": [ + { "Address": "" }, + { "Port": "22" }, + { "FuncUserName": "" }, + { "FuncPassword": "" } + ], + "Do": [ + { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "RequestTerminal": true } }, + { "Send": { "Text": "echo connected\n" } }, + { "Receive": { "Regex": "connected" } }, + { "Disconnect": {} } + ] + }, + "CheckPassword": { + "Parameters": [ + { "Address": "" }, + { "Port": "22" }, + { "AccountUserName": "" }, + { "AccountPassword": "" } + ], + "Do": [ + { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%AccountUserName%", "Password": "%AccountPassword%", "RequestTerminal": true } }, + { "Disconnect": {} } + ] + } +} +``` + +Upload and test: +```powershell +Import-SafeguardCustomPlatformScript -FilePath .\MyCompletePlatform.json +Test-SafeguardAssetConnection -AssetToUse "TestHost" -ExtendedLogging +Test-SafeguardAssetAccountPassword -AssetToUse "TestHost" -AccountToUse "testuser" -ExtendedLogging +``` + +## Step 2: Add ChangePassword + +The `ChangePassword` operation connects with the service account (which has privileges to change other users' passwords) and runs `passwd`: + +```json +"ChangePassword": { + "Parameters": [ + { "Address": "" }, + { "Port": "22" }, + { "FuncUserName": "" }, + { "FuncPassword": "" }, + { "AccountUserName": "" }, + { "AccountPassword": "" }, + { "NewPassword": "" } + ], + "Do": [ + { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "RequestTerminal": true } }, + { "Send": { "Text": "sudo passwd %AccountUserName%\n" } }, + { "Receive": { "Regex": "[Nn]ew password:|[Pp]assword:" } }, + { "Send": { "Text": "%NewPassword%\n" } }, + { "Receive": { "Regex": "[Rr]etype|[Rr]e-enter|[Cc]onfirm|[Nn]ew password:" } }, + { "Send": { "Text": "%NewPassword%\n" } }, + { "Receive": { "Regex": "successfully|updated|\\$|#" } }, + { "Disconnect": {} } + ] +} +``` + +Key points: +- `ChangePassword` connects with the **service account** (`FuncUserName`/`FuncPassword`), not the managed account. +- SPP provides `NewPassword` — you don't generate it yourself. +- The `Receive` patterns must match your target's `passwd` prompts. + +Test with: +```powershell +Invoke-SafeguardAssetAccountPasswordChange -AssetToUse "TestHost" -AccountToUse "testuser" +``` + +## Step 3: Add DiscoverAccounts + +The `DiscoverAccounts` operation connects and lists local accounts, then reports each one back to SPP using `WriteDiscoveredAccount`: + +```json +"DiscoverAccounts": { + "Parameters": [ + { "Address": "" }, + { "Port": "22" }, + { "FuncUserName": "" }, + { "FuncPassword": "" } + ], + "Do": [ + { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "RequestTerminal": false } }, + { "ExecuteCommand": { "Command": "awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {print $1}' /etc/passwd", "Output": "accounts" } }, + { "Split": { "Text": "%accounts.StdOut%", "Delimiter": "\n", "Output": "accountList" } }, + { + "ForEach": { + "Item": "acct", + "In": "%accountList%", + "Do": [ + { + "Condition": { + "If": "acct != ''", + "Then": { + "Do": [ + { "WriteDiscoveredAccount": { "AccountName": "%acct%" } } + ] + } + } + } + ] + } + }, + { "Disconnect": {} } + ] +} +``` + +Key points: +- Uses `ExecuteCommand` (batch mode with `RequestTerminal: false`) instead of interactive Send/Receive. +- Filters `/etc/passwd` for real user accounts (UID >= 1000, no nologin shell). +- Calls `WriteDiscoveredAccount` once per account — this is how SPP learns about discovered accounts. + +## Step 4: Put It All Together + +Your complete script now has four operations. Upload the final version and verify all operations work: + +```powershell +# Re-upload (replaces the previous version) +Import-SafeguardCustomPlatformScript -FilePath .\MyCompletePlatform.json + +# Test each operation +Test-SafeguardAssetConnection -AssetToUse "TestHost" -ExtendedLogging +Test-SafeguardAssetAccountPassword -AssetToUse "TestHost" -AccountToUse "testuser" -ExtendedLogging +Invoke-SafeguardAssetAccountPasswordChange -AssetToUse "TestHost" -AccountToUse "testuser" +``` + +Account discovery runs on a schedule configured in SPP — you can trigger it manually from the web UI under **Asset Management > Discovery**. + +## What SPP Knows About Your Platform + +Because your script contains these four operations, SPP automatically sets these feature flags: + +| Flag | Set because | +| --- | --- | +| `PasswordFl` | `CheckPassword` is present | +| `AccountPasswordFl` | `ChangePassword` is present | +| `AccountDiscoveryFl` | `DiscoverAccounts` is present | + +You never configure these manually — they're derived from your script. See [Feature Flags](../concepts/feature-flags.md) for the full list. + +## Next Steps + +From here you can extend your platform further: + +- **SSH key management** — Add `CheckSshKey`, `ChangeSshKey`, `DiscoverAuthorizedKeys`. See [SSH Key Management Guide](../guides/ssh-key-management.md). +- **Host key discovery** — Add `DiscoverSshHostKey`. See the [generic-linux-with-discovery](../../samples/ssh/generic-linux-with-discovery/) sample. +- **Error handling** — Wrap operations in `Try`/`Catch` for resilient behavior. See [Error Handling Guide](../guides/error-handling.md). +- **Import libraries** — Use `Imports` to pull in reusable SSH functions. See [Imports Reference](../reference/imports.md). + +For a production-ready example of everything combined, study the [GenericLinuxWithSSHKeySupport](../../samples/ssh/generic-linux-ssh-keys/) sample. From 46abe692e63b1927698d80c6cbd6a06bda6119a4 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 18:33:31 -0600 Subject: [PATCH 03/13] Fix cross-references for new directory structure - Update all internal links in guides, reference, and tutorials - Fix SampleScripts/* paths to samples/* throughout - Fix getting-started/* paths to tutorials/*, guides/*, or concepts/* - Fix guides/feature-flags.md links to concepts/feature-flags.md - Update compatibility.md relative links for new location - Update .github/workflows paths and branch target (master to main) - Update .vscode/settings.json schema fileMatch patterns - Update CONTRIBUTING.md and schema/README.md paths - Update templates/README.md content and links --- .github/workflows/validate-scripts.yml | 32 ++++++---- .vscode/settings.json | 2 +- CONTRIBUTING.md | 8 +-- docs/guides/account-discovery.md | 4 +- docs/guides/api-key-management.md | 2 +- docs/guides/development-workflow.md | 2 +- docs/guides/error-handling.md | 4 +- docs/guides/file-management.md | 2 +- docs/guides/http-platforms.md | 16 ++--- docs/guides/jit-elevation.md | 4 +- docs/guides/regex-patterns.md | 4 +- docs/guides/ssh-key-management.md | 8 +-- docs/guides/ssh-platforms.md | 22 +++---- docs/guides/testing-and-debugging.md | 4 +- docs/guides/troubleshooting.md | 12 ++-- docs/reference/commands/connect.md | 6 +- docs/reference/commands/cookies.md | 2 +- docs/reference/commands/encoding.md | 4 +- docs/reference/commands/error-handling.md | 6 +- docs/reference/commands/execute-command.md | 6 +- docs/reference/commands/flow-control.md | 14 ++-- docs/reference/commands/forms.md | 6 +- docs/reference/commands/functions.md | 6 +- docs/reference/commands/http-auth.md | 6 +- docs/reference/commands/http-setup.md | 6 +- docs/reference/commands/json.md | 6 +- docs/reference/commands/logging.md | 10 +-- docs/reference/commands/output.md | 4 +- docs/reference/commands/request.md | 6 +- docs/reference/commands/send-receive.md | 6 +- docs/reference/commands/ssh-host-key.md | 4 +- docs/reference/commands/utilities.md | 12 ++-- docs/reference/compatibility.md | 12 ++-- docs/reference/imports.md | 2 +- docs/reference/operations.md | 6 +- docs/reference/reserved-parameters.md | 4 +- docs/reference/status-messages.md | 8 +-- docs/tutorials/your-first-form-script.md | 4 +- docs/tutorials/your-first-http-script.md | 2 +- docs/tutorials/your-first-ssh-script.md | 4 +- schema/README.md | 2 +- templates/README.md | 74 +++++++++++++--------- 42 files changed, 185 insertions(+), 169 deletions(-) diff --git a/.github/workflows/validate-scripts.yml b/.github/workflows/validate-scripts.yml index 91e55f6..86f5582 100644 --- a/.github/workflows/validate-scripts.yml +++ b/.github/workflows/validate-scripts.yml @@ -2,13 +2,15 @@ name: Validate Sample Scripts on: push: - branches: [master] + branches: [main] paths: - - 'SampleScripts/**/*.json' + - 'samples/**/*.json' + - 'templates/**/*.json' pull_request: - branches: [master] + branches: [main] paths: - - 'SampleScripts/**/*.json' + - 'samples/**/*.json' + - 'templates/**/*.json' jobs: validate-json: @@ -22,19 +24,21 @@ jobs: failed=0 found=0 - while IFS= read -r -d '' f; do - found=1 + for dir in samples templates; do + while IFS= read -r -d '' f; do + found=1 - if ! jq empty "$f" >/dev/null 2>&1; then - echo "::error file=$f::Invalid JSON syntax" - echo "FAILED: $f" - jq empty "$f" - failed=1 - fi - done < <(find SampleScripts -type f -name '*.json' -print0) + if ! jq empty "$f" >/dev/null 2>&1; then + echo "::error file=$f::Invalid JSON syntax" + echo "FAILED: $f" + jq empty "$f" + failed=1 + fi + done < <(find "$dir" -type f -name '*.json' -print0) + done if [ "$found" -eq 0 ]; then - echo "No JSON files found under SampleScripts/." + echo "No JSON files found under samples/ or templates/." exit 0 fi diff --git a/.vscode/settings.json b/.vscode/settings.json index 66e0921..c21d3f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "json.schemas": [ { - "fileMatch": ["SampleScripts/**/*.json"], + "fileMatch": ["samples/**/*.json", "templates/**/*.json"], "url": "./schema/custom-platform-script.schema.json" } ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3211f7..ddc793d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,10 +18,10 @@ git checkout -b my-change ## Contributing Sample Scripts -- Add new scripts under `SampleScripts/` in the correct protocol folder: - - `SampleScripts/SSH/` - - `SampleScripts/HTTP/` - - `SampleScripts/Telnet/` +- Add new scripts under `samples/` in the correct protocol folder: + - `samples/ssh/` + - `samples/http/` + - `samples/telnet/` - Follow the JSON structure in [docs/reference/script-structure.md](docs/reference/script-structure.md). - Include the standard operations your target supports whenever possible. At minimum, include `CheckSystem` (the operation behind **Test Connection**) and `CheckPassword`, unless accounts change their own passwords on that platform (in which case `CheckSystem` may not apply). - Use meaningful parameter names and sensible defaults. diff --git a/docs/guides/account-discovery.md b/docs/guides/account-discovery.md index a81010a..f38234f 100644 --- a/docs/guides/account-discovery.md +++ b/docs/guides/account-discovery.md @@ -523,7 +523,7 @@ If that restriction changes in the future, the related pattern would be similar - [Operations Reference](../reference/operations.md) - [Output Commands](../reference/commands/output.md) - [Command Index](../reference/commands/index.md) -- [Your First SSH Script](../getting-started/your-first-ssh-script.md) -- [Your First HTTP Script](../getting-started/your-first-http-script.md) +- [Your First SSH Script](../tutorials/your-first-ssh-script.md) +- [Your First HTTP Script](../tutorials/your-first-http-script.md) - [SSH Platforms Guide](ssh-platforms.md) - [HTTP Platforms Guide](http-platforms.md) diff --git a/docs/guides/api-key-management.md b/docs/guides/api-key-management.md index ca0eee2..75dc543 100644 --- a/docs/guides/api-key-management.md +++ b/docs/guides/api-key-management.md @@ -413,5 +413,5 @@ Design your script so these cases produce a clear error message and never report - [Operations Reference](../reference/operations.md) - [Reserved Parameters](../reference/reserved-parameters.md) - [HTTP/REST API Platforms](http-platforms.md) -- [Your First HTTP Script](../getting-started/your-first-http-script.md) +- [Your First HTTP Script](../tutorials/your-first-http-script.md) - [Output Commands](../reference/commands/output.md) diff --git a/docs/guides/development-workflow.md b/docs/guides/development-workflow.md index 4e80b93..ef5d108 100644 --- a/docs/guides/development-workflow.md +++ b/docs/guides/development-workflow.md @@ -38,7 +38,7 @@ In practice, you will repeat this cycle many times while you refine connectivity Start with a known-good base whenever possible. -- Begin from a template in `SampleScripts/Templates/` or adapt an existing sample script that is close to your target system. +- Begin from a template in `templates/` or adapt an existing sample script that is close to your target system. - Use a JSON-aware editor so syntax errors are caught before upload. - Refer to [Script Structure](../reference/script-structure.md) for the required format, top-level keys, operations, and `Do` blocks. - Validate early with `Test-SafeguardCustomPlatformScript` before you even create a custom platform on the appliance. diff --git a/docs/guides/error-handling.md b/docs/guides/error-handling.md index 9c2c2de..53b6bd4 100644 --- a/docs/guides/error-handling.md +++ b/docs/guides/error-handling.md @@ -80,7 +80,7 @@ A few important points: - `Try` also supports `Finally`, which always runs after `Do` or `Catch`. This is useful for cleanup. - `Finally` cannot `Return` or `Break`. If `Finally` throws, that cleanup error replaces the earlier result. -For syntax details, see [Error Handling Commands](../reference/commands/error-handling.md). For real examples, compare [`GenericLinux.json`](../../SampleScripts/SSH/GenericLinux.json), [`CustomFacebook.json`](../../SampleScripts/HTTP/CustomFacebook.json), and [`OneLogin_GRC_JIT_addon.json`](../../SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json). +For syntax details, see [Error Handling Commands](../reference/commands/error-handling.md). For real examples, compare [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json), [`CustomFacebook.json`](../../samples/http/facebook/CustomFacebook.json), and [`OneLogin_GRC_JIT_addon.json`](../../samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json). ## Choosing between `Return true`, `Return false`, and `Throw` @@ -582,7 +582,7 @@ For broader HTTP patterns, see [HTTP/REST API Platforms](http-platforms.md). ## Using `ExitStatusBufferName` and `Condition` -For SSH batch-mode scripts, [`ExitStatusBufferName`](../reference/commands/execute-command.md) is the standard way to capture a command's numeric exit code. Pair it with [`Condition`](../reference/commands/flow-control.md) so the script can decide whether the result means success, failure, or error. [`LinuxSshBatchModeExample.json`](../../SampleScripts/SSH/LinuxSshBatchModeExample.json) is a good end-to-end sample of this pattern. +For SSH batch-mode scripts, [`ExitStatusBufferName`](../reference/commands/execute-command.md) is the standard way to capture a command's numeric exit code. Pair it with [`Condition`](../reference/commands/flow-control.md) so the script can decide whether the result means success, failure, or error. [`LinuxSshBatchModeExample.json`](../../samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json) is a good end-to-end sample of this pattern. ```json [ diff --git a/docs/guides/file-management.md b/docs/guides/file-management.md index 3d6d035..4caa7dd 100644 --- a/docs/guides/file-management.md +++ b/docs/guides/file-management.md @@ -484,7 +484,7 @@ File-based credentials are often long-lived, high-value secrets. Handle them lik - [Reserved Parameters Reference](../reference/reserved-parameters.md#file-management) - [SSH Platforms Guide](ssh-platforms.md) - [HTTP Platforms Guide](http-platforms.md) -- [Your First SSH Script](../getting-started/your-first-ssh-script.md) +- [Your First SSH Script](../tutorials/your-first-ssh-script.md) - [Variables Reference](../reference/variables.md) - [ExecuteCommand](../reference/commands/execute-command.md) - [Request](../reference/commands/request.md) diff --git a/docs/guides/http-platforms.md b/docs/guides/http-platforms.md index e72e709..2cddbf5 100644 --- a/docs/guides/http-platforms.md +++ b/docs/guides/http-platforms.md @@ -112,7 +112,7 @@ For APIs that accept a username and password on every request, use [`HttpAuth`]( ] ``` -This is the pattern used by `SampleScripts/HTTP/WordPressHttp.json`. +This is the pattern used by `samples/http/wordpress/WordPressHttp.json`. > [!IMPORTANT] > [`HttpAuth`](../reference/commands/http-auth.md) currently supports `Basic` and `Digest`. For `Bearer` tokens and most API-key schemes, add the header yourself with [`Headers`](../reference/commands/http-setup.md). @@ -184,7 +184,7 @@ A common pattern is: ] ``` -This is the same overall shape used in `SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json`, which stores a token and then sends `Authorization: "Bearer %AccessToken%"` on later `Request` commands. +This is the same overall shape used in `samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json`, which stores a token and then sends `Authorization: "Bearer %AccessToken%"` on later `Request` commands. ### API keys in headers @@ -215,7 +215,7 @@ If the API expects an API key instead of a bearer token, add it with [`Headers`] ] ``` -That is the same style used in `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`. +That is the same style used in `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json`. The header name can be anything the API requires: @@ -235,7 +235,7 @@ Some systems do not expose a clean REST login endpoint. Instead, they expect the 5. `POST` the form. 6. Reuse the resulting cookies on later requests. -This is the pattern used by `SampleScripts/HTTP/CustomTwitter.json` and `SampleScripts/HTTP/CustomFacebook.json`. +This is the pattern used by `samples/http/twitter/CustomTwitter.json` and `samples/http/facebook/CustomFacebook.json`. ## Common end-to-end flow @@ -552,7 +552,7 @@ Practical guidance: If the page depends on JavaScript to build the real request, inspect the browser traffic and target the underlying HTTP endpoint directly. Safeguard cannot run the page's JavaScript for you. -For a full walkthrough, see [Your First Form Script](../getting-started/your-first-form-script.md). +For a full walkthrough, see [Your First Form Script](../tutorials/your-first-form-script.md). ## Cookie management @@ -715,7 +715,7 @@ Build a URL with `limit` and an offset or page number, then loop until the curre ### Link-header or cursor pagination -Some APIs return a `Link` header or a `next` cursor instead of page numbers. `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json` shows this style: it reads the `Link` header and loops until there is no next link. +Some APIs return a `Link` header or a `next` cursor instead of page numbers. `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json` shows this style: it reads the `Link` header and loops until there is no next link. Use this pattern when: @@ -892,7 +892,7 @@ Use this guide together with the command reference pages: - [`JSON`](../reference/commands/json.md) - [`Flow Control`](../reference/commands/flow-control.md) - [`Error Handling`](../reference/commands/error-handling.md) -- [Your First HTTP Script](../getting-started/your-first-http-script.md) -- [Your First Form Script](../getting-started/your-first-form-script.md) +- [Your First HTTP Script](../tutorials/your-first-http-script.md) +- [Your First Form Script](../tutorials/your-first-form-script.md) When you are building a new HTTP platform, start simple: verify connectivity with one authenticated `GET`, confirm the response parsing works, and only then add multi-step login, pagination, retries, and cleanup logic. diff --git a/docs/guides/jit-elevation.md b/docs/guides/jit-elevation.md index 29ab5d4..f27f2d0 100644 --- a/docs/guides/jit-elevation.md +++ b/docs/guides/jit-elevation.md @@ -545,8 +545,8 @@ The user never needs to know whether the target-side change was done with SSH co The repository already includes a JIT-focused OneLogin sample: -- [`../../SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json`](../../SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json) -- [`../../SampleScripts/HTTP/README.md`](../../SampleScripts/HTTP/README.md) +- [`../../samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json`](../../samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json) +- [`../../samples/http/README.md`](../../samples/http/README.md) That sample is useful because it implements both pairs: diff --git a/docs/guides/regex-patterns.md b/docs/guides/regex-patterns.md index fb1218e..b7f0d94 100644 --- a/docs/guides/regex-patterns.md +++ b/docs/guides/regex-patterns.md @@ -307,7 +307,7 @@ Use these patterns as starting points. Adjust them to the exact output your targ - **Be careful with greedy `.*`.** Use `.*?` when you are matching between start and end markers. - **Strip ANSI when needed.** Colored prompts can make a correct prompt regex look broken. - **Keep parsing in small steps.** Capture into `match`, inspect `match.Success`, then read named groups. -- **Use the local test workflow before upload.** See [Testing and Debugging](../getting-started/testing-and-debugging.md) and the repository's `tools\TestTool.ps1`. +- **Use the local test workflow before upload.** See [Testing and Debugging](../guides/testing-and-debugging.md) and the repository's `tools\TestTool.ps1`. ## Related references @@ -317,4 +317,4 @@ Use these patterns as starting points. Adjust them to the exact output your targ - [Variables Reference](../reference/variables.md) - [SSH Platforms Guide](ssh-platforms.md) - [Error Handling Guide](error-handling.md) -- [Testing and Debugging](../getting-started/testing-and-debugging.md) +- [Testing and Debugging](../guides/testing-and-debugging.md) diff --git a/docs/guides/ssh-key-management.md b/docs/guides/ssh-key-management.md index 66d44ad..6fda995 100644 --- a/docs/guides/ssh-key-management.md +++ b/docs/guides/ssh-key-management.md @@ -103,7 +103,7 @@ Do not assume only one path exists. A practical Unix pattern is to check multipl - `%h/.ssh/authorized_keys` - `%h/.ssh/authorized_keys2` -[`GenericLinuxWithSSHKeySupport.json`](../../SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json) follows exactly that pattern and resolves the final paths before check, change, or discovery. +[`GenericLinuxWithSSHKeySupport.json`](../../samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json) follows exactly that pattern and resolves the final paths before check, change, or discovery. ### 2. Check for a key with exact matching @@ -154,7 +154,7 @@ A simplified append step looks like this: } ``` -The sample [`GenericLinuxWithSSHKeySupport.json`](../../SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json) uses the same overall approach, including backup and rollback. +The sample [`GenericLinuxWithSSHKeySupport.json`](../../samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json) uses the same overall approach, including backup and rollback. ### 4. Remove the old key by rewriting the file @@ -288,7 +288,7 @@ Restricted keys are keys with option prefixes such as: - `no-port-forwarding` - `restrict` -The repository's [`RestrictedAuthorizedKeyExample.json`](../../SampleScripts/SSH/RestrictedAuthorizedKeyExample.json) shows the broader pattern of using a restricted key for service-account authentication. For target-account key management, the important lesson is the same: do not assume a key line starts with `ssh-rsa` or `ssh-ed25519`. +The repository's [`RestrictedAuthorizedKeyExample.json`](../../samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json) shows the broader pattern of using a restricted key for service-account authentication. For target-account key management, the important lesson is the same: do not assume a key line starts with `ssh-rsa` or `ssh-ed25519`. A good parser anchors on the key type but allows an optional prefix before it: @@ -390,6 +390,6 @@ During `DiscoverAuthorizedKeys`, it is fine to skip blank or malformed lines. Bu - [Reserved Parameters](../reference/reserved-parameters.md) - [SSH Platforms Guide](ssh-platforms.md) - [Imports](../reference/imports.md) -- [Your First SSH Script](../getting-started/your-first-ssh-script.md) +- [Your First SSH Script](../tutorials/your-first-ssh-script.md) - [Account Discovery](account-discovery.md) - [Output Commands](../reference/commands/output.md) diff --git a/docs/guides/ssh-platforms.md b/docs/guides/ssh-platforms.md index 61b4e7a..b038663 100644 --- a/docs/guides/ssh-platforms.md +++ b/docs/guides/ssh-platforms.md @@ -31,13 +31,13 @@ As a rule: - Use **interactive SSH** when you must react to prompts one step at a time. - Use **batch mode** when the remote system can do the whole job with normal command execution. -- Prefer **sample-first development**. Start from a close match in [`SampleScripts/SSH`](../../SampleScripts/SSH/) instead of building from scratch. +- Prefer **sample-first development**. Start from a close match in [`samples/SSH`](../../samples/ssh/) instead of building from scratch. ## Connection and login patterns ### Interactive expect-style pattern -This is the classic Linux pattern used in [`GenericLinux.json`](../../SampleScripts/SSH/GenericLinux.json): connect, flush the banner, set up the shell, send a command, receive output, and react to prompts. +This is the classic Linux pattern used in [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json): connect, flush the banner, set up the shell, send a command, receive output, and react to prompts. ```json { @@ -109,7 +109,7 @@ Use this pattern when: - the target behaves like a normal SSH command runner - you do not need a PTY - you want cleaner stdout, stderr, and exit-code handling -- you are working from samples such as [`LinuxSshBatchModeExample.json`](../../SampleScripts/SSH/LinuxSshBatchModeExample.json) or [`RestrictedAuthorizedKeyExample.json`](../../SampleScripts/SSH/RestrictedAuthorizedKeyExample.json) +- you are working from samples such as [`LinuxSshBatchModeExample.json`](../../samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json) or [`RestrictedAuthorizedKeyExample.json`](../../samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json) ## Using `Connect`, `Disconnect`, `Send`, `Receive`, and `ExecuteCommand` @@ -474,22 +474,22 @@ The Linux samples set environment variables so prompts are more predictable and - For interactive shells, detect a `sudo` or `su` password prompt with `Receive`. - For batch mode, use `ExecuteCommand` and inspect `stderr` and `rc`. - If `sudo` requires a password, decide whether to support that path explicitly or require password-less sudo for the service account. -- If your script uses a restricted authorized key, follow the batch-mode patterns from [`RestrictedAuthorizedKeyExample.json`](../../SampleScripts/SSH/RestrictedAuthorizedKeyExample.json). +- If your script uses a restricted authorized key, follow the batch-mode patterns from [`RestrictedAuthorizedKeyExample.json`](../../samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json). ## Sample scripts to study Start with the closest example in this repository: -- [`GenericLinux.json`](../../SampleScripts/SSH/GenericLinux.json) - interactive Linux password workflows -- [`GenericLinuxWithSSHKeySupport.json`](../../SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json) - interactive Linux plus SSH key management -- [`GenericLinuxWithDiscovery.json`](../../SampleScripts/SSH/GenericLinuxWithDiscovery.json) - interactive Linux plus discovery helpers -- [`LinuxSshBatchModeExample.json`](../../SampleScripts/SSH/LinuxSshBatchModeExample.json) - non-interactive `ExecuteCommand` pattern -- [`RestrictedAuthorizedKeyExample.json`](../../SampleScripts/SSH/RestrictedAuthorizedKeyExample.json) - restricted-key SSH authentication with batch-mode command execution -- [`vCenterServerAppliance.json`](../../SampleScripts/SSH/vCenterServerAppliance.json) - appliance-specific interactive flow with timing considerations +- [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json) - interactive Linux password workflows +- [`GenericLinuxWithSSHKeySupport.json`](../../samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json) - interactive Linux plus SSH key management +- [`GenericLinuxWithDiscovery.json`](../../samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json) - interactive Linux plus discovery helpers +- [`LinuxSshBatchModeExample.json`](../../samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json) - non-interactive `ExecuteCommand` pattern +- [`RestrictedAuthorizedKeyExample.json`](../../samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json) - restricted-key SSH authentication with batch-mode command execution +- [`vCenterServerAppliance.json`](../../samples/ssh/vcenter-appliance/vCenterServerAppliance.json) - appliance-specific interactive flow with timing considerations ## Related references -- [Your First SSH Script](../getting-started/your-first-ssh-script.md) +- [Your First SSH Script](../tutorials/your-first-ssh-script.md) - [Connect and Disconnect](../reference/commands/connect.md) - [Send and Receive](../reference/commands/send-receive.md) - [ExecuteCommand](../reference/commands/execute-command.md) diff --git a/docs/guides/testing-and-debugging.md b/docs/guides/testing-and-debugging.md index 8dbb9fe..6f9f53f 100644 --- a/docs/guides/testing-and-debugging.md +++ b/docs/guides/testing-and-debugging.md @@ -162,7 +162,7 @@ You can also check the custom parameters the platform exposes: Get-SafeguardCustomPlatformScriptParameter "My Custom Platform" ``` -If a capability is missing, compare the uploaded script against the rules in [Platform Feature Flags](../guides/feature-flags.md). Many "why does SPP not show this option?" problems are really operation-name or reserved-parameter-name problems. +If a capability is missing, compare the uploaded script against the rules in [Platform Feature Flags](../concepts/feature-flags.md). Many "why does SPP not show this option?" problems are really operation-name or reserved-parameter-name problems. ## Tips @@ -176,6 +176,6 @@ If a capability is missing, compare the uploaded script against the rules in [Pl ## Next Steps - [Operations Reference](../reference/operations.md) for the full operation list. -- [Platform Feature Flags](../guides/feature-flags.md) for verifying derived capabilities after upload. +- [Platform Feature Flags](../concepts/feature-flags.md) for verifying derived capabilities after upload. - [Troubleshooting](../guides/troubleshooting.md) for deeper error-resolution guidance. - [Commands Reference](../reference/commands/index.md) for the commands available inside `Do` blocks. diff --git a/docs/guides/troubleshooting.md b/docs/guides/troubleshooting.md index 149af62..e972c3d 100644 --- a/docs/guides/troubleshooting.md +++ b/docs/guides/troubleshooting.md @@ -4,7 +4,7 @@ When a custom platform operation fails, start here. -For the exact PowerShell cmdlets, REST calls, and UI steps for running operations with extended logging, see [Testing and Debugging](../getting-started/testing-and-debugging.md). +For the exact PowerShell cmdlets, REST calls, and UI steps for running operations with extended logging, see [Testing and Debugging](../guides/testing-and-debugging.md). ## Quick Triage @@ -18,7 +18,7 @@ Use this table first. It covers the failure modes people usually search for. | Parameter type mismatch | A reserved parameter is declared with the wrong `Type`, such as `Port` as `String` instead of `Integer` | Check [Reserved Parameters](../reference/reserved-parameters.md) and correct both the parameter name and type. | | Operation timeout | The target system is unreachable or responding too slowly | Check network connectivity, increase the `Timeout` parameter, and verify `Address` and `Port`. | | Task failure with no useful error | Default logging is too sparse to show where the script failed | Re-run the same operation with `?extendedLogging=true` or PowerShell `-ExtendedLogging` so the task log includes command-by-command detail. | -| Feature flags not appearing after upload | The script is missing the required operation and parameter combination for that flag | See [Feature Flags](../guides/feature-flags.md) for the exact operation and reserved parameter each flag requires. | +| Feature flags not appearing after upload | The script is missing the required operation and parameter combination for that flag | See [Feature Flags](../concepts/feature-flags.md) for the exact operation and reserved parameter each flag requires. | | `Function not found` at runtime | A call references a function that is not defined in the script or any imported library | Check the function name spelling and verify the required import library is listed. | | Connection refused or timeout on `Connect` | Wrong port, firewall policy, or target service is not running | Verify the `Port` parameter, confirm network reachability, and make sure the target service is listening. | | Password change succeeds but `CheckPassword` fails | The new password was not actually applied on the target | Review the `ChangePassword` logic and verify the password-change command returned a successful exit status. | @@ -28,7 +28,7 @@ Use this table first. It covers the failure modes people usually search for. ## Debugging Workflow 1. **Enable extended logging.** - Use the same operation that is failing, but run it with extended logging enabled. See [Testing and Debugging](../getting-started/testing-and-debugging.md) for the full workflow. + Use the same operation that is failing, but run it with extended logging enabled. See [Testing and Debugging](../guides/testing-and-debugging.md) for the full workflow. ```powershell Test-SafeguardAsset "Test Target" -ExtendedLogging @@ -131,15 +131,15 @@ If these fail, fix connectivity first. The script cannot recover from a blocked If a script behaves as if values are missing, verify that you used the exact reserved parameter names and types documented in [Reserved Parameters](../reference/reserved-parameters.md). Then confirm the expected values actually exist on the asset, account, or profile. -Good examples to verify are `Address`, `Port`, `Timeout`, `AccountUserName`, `FuncUserName`, and `UseSsl`. If a built-in field or workflow is missing, compare the script against [Feature Flags](../guides/feature-flags.md) and [Operations Reference](../reference/operations.md). +Good examples to verify are `Address`, `Port`, `Timeout`, `AccountUserName`, `FuncUserName`, and `UseSsl`. If a built-in field or workflow is missing, compare the script against [Feature Flags](../concepts/feature-flags.md) and [Operations Reference](../reference/operations.md). ## Getting Help - Browse the [SafeguardCustomPlatform repository](https://github.com/OneIdentity/SafeguardCustomPlatform) for sample scripts and existing documentation. - Ask questions in the [One Identity Community forums](https://www.oneidentity.com/community/). - Keep these references open while debugging: - - [Testing and Debugging](../getting-started/testing-and-debugging.md) + - [Testing and Debugging](../guides/testing-and-debugging.md) - [Reserved Parameters](../reference/reserved-parameters.md) - - [Feature Flags](../guides/feature-flags.md) + - [Feature Flags](../concepts/feature-flags.md) - [Operations Reference](../reference/operations.md) - [Commands Reference](../reference/commands/index.md) diff --git a/docs/reference/commands/connect.md b/docs/reference/commands/connect.md index a70797a..fb1f74d 100644 --- a/docs/reference/commands/connect.md +++ b/docs/reference/commands/connect.md @@ -73,7 +73,7 @@ Closes an existing connection object. ### SSH connection with password authentication -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -95,7 +95,7 @@ From `SampleScripts/SSH/GenericLinux.json`: ### SSH connection with key authentication -From `SampleScripts/SSH/RestrictedAuthorizedKeyExample.json`: +From `samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json`: ```json { @@ -117,7 +117,7 @@ From `SampleScripts/SSH/RestrictedAuthorizedKeyExample.json`: ### Telnet connection -From `SampleScripts/Telnet/GenericCiscoIosTelnet.json`: +From `samples/telnet/cisco-ios/GenericCiscoIosTelnet.json`: ```json { diff --git a/docs/reference/commands/cookies.md b/docs/reference/commands/cookies.md index 61a544d..2ed7fa6 100644 --- a/docs/reference/commands/cookies.md +++ b/docs/reference/commands/cookies.md @@ -10,7 +10,7 @@ Most browser-style login flows do not need these commands on every step because A typical login flow simply relies on `Request` with the default `PersistCookies: true`. -From `SampleScripts/HTTP/CustomTwitter.json`: +From `samples/http/twitter/CustomTwitter.json`: ```json { diff --git a/docs/reference/commands/encoding.md b/docs/reference/commands/encoding.md index be3bd07..504f93d 100644 --- a/docs/reference/commands/encoding.md +++ b/docs/reference/commands/encoding.md @@ -129,7 +129,7 @@ From the built-in `System/Imports/CheckpointSshFunctions.json` definition: ### Compare against a Linux shadow entry -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -143,7 +143,7 @@ From `SampleScripts/SSH/GenericLinux.json`: ### Compare against a general salted hash -From `SampleScripts/SSH/LinuxSshBatchModeExample.json`: +From `samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json`: ```json { diff --git a/docs/reference/commands/error-handling.md b/docs/reference/commands/error-handling.md index 7348876..705eb3f 100644 --- a/docs/reference/commands/error-handling.md +++ b/docs/reference/commands/error-handling.md @@ -51,7 +51,7 @@ Wrap risky work in `Try`. If the `Do` block throws an error, the optional `Catch #### Wrap a connection attempt -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -82,7 +82,7 @@ From `SampleScripts/SSH/GenericLinux.json`: #### Clean up before failing -From `SampleScripts/HTTP/CustomFacebook.json`: +From `samples/http/facebook/CustomFacebook.json`: ```json { @@ -117,7 +117,7 @@ Use `Throw` inside a `Catch` block to re-raise the current error or to wrap it i ### Example -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { diff --git a/docs/reference/commands/execute-command.md b/docs/reference/commands/execute-command.md index 213feba..85a53f6 100644 --- a/docs/reference/commands/execute-command.md +++ b/docs/reference/commands/execute-command.md @@ -71,7 +71,7 @@ It is the batch-mode alternative to interactive `Send`/`Receive`: one call sends ### `RunCommand` wrapper from the batch-mode Linux sample -From `SampleScripts/SSH/LinuxSshBatchModeExample.json`: +From `samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json`: ```json { @@ -91,7 +91,7 @@ From `SampleScripts/SSH/LinuxSshBatchModeExample.json`: ### Connect for batch mode first -From `SampleScripts/SSH/LinuxSshBatchModeExample.json`: +From `samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json`: ```json { @@ -113,7 +113,7 @@ From `SampleScripts/SSH/LinuxSshBatchModeExample.json`: ### Execute a sudo command with SSH key authentication -From `SampleScripts/SSH/RestrictedAuthorizedKeyExample.json`: +From `samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json`: ```json { diff --git a/docs/reference/commands/flow-control.md b/docs/reference/commands/flow-control.md index 529bf66..c00d33c 100644 --- a/docs/reference/commands/flow-control.md +++ b/docs/reference/commands/flow-control.md @@ -38,7 +38,7 @@ Use `Condition` when a single expression decides whether a block runs. `Else` is #### Simple conditional return -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -56,7 +56,7 @@ From `SampleScripts/SSH/GenericLinux.json`: #### `Else` branch inside a default handler -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -122,7 +122,7 @@ Use `Switch` when one value needs to be matched against multiple cases. String ` #### Switch on a returned terminal status -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -154,7 +154,7 @@ From `SampleScripts/SSH/GenericLinux.json`: #### Default-case fallback logic -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -237,7 +237,7 @@ Use `For` as a while-style loop by supplying `Condition` and `Body` and omitting ### Example -From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: +From `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json`: ```json { @@ -297,7 +297,7 @@ Use `ForEach` when a variable already contains an enumerable collection and you ### Example -From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: +From `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ```json { @@ -351,7 +351,7 @@ Use `Return` to exit the current function or operation and optionally pass back ### Example -From `SampleScripts/Telnet/GenericCiscoIosTelnet.json`: +From `samples/telnet/cisco-ios/GenericCiscoIosTelnet.json`: ```json { diff --git a/docs/reference/commands/forms.md b/docs/reference/commands/forms.md index 55e8683..ef475fe 100644 --- a/docs/reference/commands/forms.md +++ b/docs/reference/commands/forms.md @@ -95,7 +95,7 @@ The usual pattern is: request the page, extract the form, inspect or modify fiel ### Extract the login form from a page -From `SampleScripts/HTTP/CustomTwitter.json`: +From `samples/http/twitter/CustomTwitter.json`: ```json { @@ -118,7 +118,7 @@ From `SampleScripts/HTTP/CustomTwitter.json`: ### Populate fields before posting the form -From `SampleScripts/HTTP/CustomTwitter.json`: +From `samples/http/twitter/CustomTwitter.json`: ```json { @@ -157,7 +157,7 @@ From the built-in `System/Twitter.json` platform definition: ### Submit the extracted form -From `SampleScripts/HTTP/CustomTwitter.json`: +From `samples/http/twitter/CustomTwitter.json`: ```json { diff --git a/docs/reference/commands/functions.md b/docs/reference/commands/functions.md index 1f16413..b5e519d 100644 --- a/docs/reference/commands/functions.md +++ b/docs/reference/commands/functions.md @@ -61,7 +61,7 @@ Use the top-level `Functions` array to define reusable blocks that can be called ### Example -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -124,7 +124,7 @@ Use the `Function` command inside a `Do` block to invoke a named function. In cu #### Call a helper and capture its result -From `SampleScripts/Telnet/GenericCiscoIosTelnet.json`: +From `samples/telnet/cisco-ios/GenericCiscoIosTelnet.json`: ```json { @@ -138,7 +138,7 @@ From `SampleScripts/Telnet/GenericCiscoIosTelnet.json`: #### Call a function with a secret argument -From `SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json`: +From `samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json`: ```json { diff --git a/docs/reference/commands/http-auth.md b/docs/reference/commands/http-auth.md index 4ac5d47..e460220 100644 --- a/docs/reference/commands/http-auth.md +++ b/docs/reference/commands/http-auth.md @@ -62,7 +62,7 @@ Current script-engine support is focused on `Basic` and `Digest` authentication. ### Basic auth against a WordPress REST API -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -82,7 +82,7 @@ From `SampleScripts/HTTP/WordPressHttp.json`: ### Basic auth with managed-account credentials -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -99,7 +99,7 @@ From `SampleScripts/HTTP/WordPressHttp.json`: ### Token-style authorization via `Headers` -From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: +From `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ```json { diff --git a/docs/reference/commands/http-setup.md b/docs/reference/commands/http-setup.md index 8f22178..952ec17 100644 --- a/docs/reference/commands/http-setup.md +++ b/docs/reference/commands/http-setup.md @@ -98,7 +98,7 @@ Adds one or more headers to a named request object. ### Build a WordPress request object after selecting HTTP or HTTPS -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -123,7 +123,7 @@ From `SampleScripts/HTTP/WordPressHttp.json`: ### Add token-style authorization before an API request -From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: +From `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ```json { @@ -138,7 +138,7 @@ From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ### Set multiple custom headers for an authentication endpoint -From `SampleScripts/HTTP/Forgerock_OpenAM.json`: +From `samples/http/forgerock-openam/Forgerock_OpenAM.json`: ```json { diff --git a/docs/reference/commands/json.md b/docs/reference/commands/json.md index a01a048..235a062 100644 --- a/docs/reference/commands/json.md +++ b/docs/reference/commands/json.md @@ -40,7 +40,7 @@ After parsing, expressions can inspect the object directly. ### Parse a WordPress users response into a collection -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -71,7 +71,7 @@ From `SampleScripts/HTTP/WordPressHttp.json`: ### Parse an Okta API response and return a property -From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: +From `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ```json { @@ -94,7 +94,7 @@ From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ### Parse ForgeRock authentication output -From `SampleScripts/HTTP/Forgerock_OpenAM.json`: +From `samples/http/forgerock-openam/Forgerock_OpenAM.json`: ```json { diff --git a/docs/reference/commands/logging.md b/docs/reference/commands/logging.md index 3fdfbb5..c9bfeee 100644 --- a/docs/reference/commands/logging.md +++ b/docs/reference/commands/logging.md @@ -30,7 +30,7 @@ Use `Log` to add a message to the task log from script logic. #### Log a state transition -From `SampleScripts/SSH/vCenterServerAppliance.json`: +From `samples/ssh/vcenter-appliance/vCenterServerAppliance.json`: ```json { "Log": { "Text": "Entered root shell" } } @@ -38,7 +38,7 @@ From `SampleScripts/SSH/vCenterServerAppliance.json`: #### Log a task-specific warning -From `SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json`: +From `samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json`: ```json { @@ -66,7 +66,7 @@ Current scripts and current engine source do not expose a separate `Trace` comma ### Example -From `SampleScripts/SSH/vCenterServerAppliance.json`: +From `samples/ssh/vcenter-appliance/vCenterServerAppliance.json`: ```json { "Log": { "Text": "No users discovered" } } @@ -104,7 +104,7 @@ Use `Status` to publish progress and a localized message while the task is runni #### Report connection-test progress -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -121,7 +121,7 @@ From `SampleScripts/SSH/GenericLinux.json`: #### Report discovery progress -From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: +From `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json`: ```json { diff --git a/docs/reference/commands/output.md b/docs/reference/commands/output.md index a819e33..cd0bc64 100644 --- a/docs/reference/commands/output.md +++ b/docs/reference/commands/output.md @@ -28,7 +28,7 @@ Use `WriteResponseObject` to emit a final response value or object. ### Example -From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: +From `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json`: ```json { "WriteResponseObject": { "Value": "%HostKey::$%" } } @@ -66,7 +66,7 @@ Current scripts and current engine source do not expose a separate `WriteRespons ### Example -Sample scripts typically return the completed value directly rather than writing individual properties. For example, `SampleScripts/SSH/GenericLinuxWithDiscovery.json` returns the discovered host key in one step: +Sample scripts typically return the completed value directly rather than writing individual properties. For example, `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json` returns the discovered host key in one step: ```json { "WriteResponseObject": { "Value": "%HostKey::$%" } } diff --git a/docs/reference/commands/request.md b/docs/reference/commands/request.md index da21308..362d134 100644 --- a/docs/reference/commands/request.md +++ b/docs/reference/commands/request.md @@ -72,7 +72,7 @@ When `ResponseObjectName` is set, later expressions can read these commonly used ### Simple GET request with Basic auth context -From `SampleScripts/HTTP/WordPressHttp.json`: +From `samples/http/wordpress/WordPressHttp.json`: ```json { @@ -92,7 +92,7 @@ From `SampleScripts/HTTP/WordPressHttp.json`: ### POST a JSON body to an API -From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: +From `samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ```json { @@ -123,7 +123,7 @@ From `SampleScripts/HTTP/Okta_WithDiscoveryAndGroupMembershipRestore.json`: ### Inspect redirect headers after a login form post -From `SampleScripts/HTTP/CustomTwitter.json`: +From `samples/http/twitter/CustomTwitter.json`: ```json { diff --git a/docs/reference/commands/send-receive.md b/docs/reference/commands/send-receive.md index d50200f..7b39f5d 100644 --- a/docs/reference/commands/send-receive.md +++ b/docs/reference/commands/send-receive.md @@ -68,7 +68,7 @@ Reads output from an open interactive connection and stores it in a buffer varia ### Set up the shell and flush the banner -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -82,7 +82,7 @@ From `SampleScripts/SSH/GenericLinux.json`: ### Use `%DelegationPrefix%` and answer a sudo prompt -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -107,7 +107,7 @@ From `SampleScripts/SSH/GenericLinux.json`: ### Interactive password-change loop -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { "Send": { "ConnectionObjectName": "ConnectSsh", "Buffer": "%NewPassword%", "ContainsSecret": true } } diff --git a/docs/reference/commands/ssh-host-key.md b/docs/reference/commands/ssh-host-key.md index 573bc01..11bd316 100644 --- a/docs/reference/commands/ssh-host-key.md +++ b/docs/reference/commands/ssh-host-key.md @@ -49,7 +49,7 @@ Use it inside the `DiscoverSshHostKey` or `RetrieveSshHostKey` operations to cap ### Asset host-key discovery with software version capture -From `SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json`: +From `samples/ssh/generic-linux-ssh-keys/GenericLinuxWithSSHKeySupport.json`: ```json { @@ -66,7 +66,7 @@ From `SampleScripts/SSH/GenericLinuxWithSSHKeySupport.json`: ### Minimal host-key discovery -From `SampleScripts/SSH/LinuxSshBatchModeExample.json`: +From `samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json`: ```json { diff --git a/docs/reference/commands/utilities.md b/docs/reference/commands/utilities.md index e6822ed..53ae966 100644 --- a/docs/reference/commands/utilities.md +++ b/docs/reference/commands/utilities.md @@ -33,7 +33,7 @@ Use `SetItem` to create or replace a variable. `Declare` is an alias that uses t #### Store a value at local scope -From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: +From `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json`: ```json { "SetItem": { "Name": "DiscoveryResult", "Value": true } } @@ -41,7 +41,7 @@ From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: #### Update a parsed match object during discovery -From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: +From `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json`: ```json { "SetItem": { "Name": "match", "Value": "%{match.NextMatch()}%" } } @@ -65,7 +65,7 @@ Use `Comment` to leave a note in the JSON without affecting runtime behavior. ### Example -From `SampleScripts/Telnet/GenericCiscoIosTelnet.json`: +From `samples/telnet/cisco-ios/GenericCiscoIosTelnet.json`: ```json { @@ -95,7 +95,7 @@ Use `Wait` when the script must pause before retrying or reading a follow-up res #### Short fixed delay -From `SampleScripts/SSH/vCenterServerAppliance.json`: +From `samples/ssh/vcenter-appliance/vCenterServerAppliance.json`: ```json { "Wait": { "Seconds": 1 } } @@ -103,7 +103,7 @@ From `SampleScripts/SSH/vCenterServerAppliance.json`: #### Delay driven by a retry variable -From `SampleScripts/HTTP/OneLogin_GRC_JIT_addon.json`: +From `samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json`: ```json { "Wait": { "Seconds": "%{RetrySeconds}%" } } @@ -141,7 +141,7 @@ Use `WriteDiscoveredAccount` during account discovery to emit one discovered-acc ### Example -From `SampleScripts/SSH/GenericLinuxWithDiscovery.json`: +From `samples/ssh/generic-linux-with-discovery/GenericLinuxWithDiscovery.json`: ```json { diff --git a/docs/reference/compatibility.md b/docs/reference/compatibility.md index 5e2816d..414c1de 100644 --- a/docs/reference/compatibility.md +++ b/docs/reference/compatibility.md @@ -1,10 +1,10 @@ -[← Documentation](README.md) +[← Reference](README.md) # SPP Compatibility Matrix Safeguard custom platform capabilities are derived from the operations, reserved parameter names, and engine features present in your script. Version compatibility matters because a script can be valid JSON and still depend on a capability your SPP appliance does not support yet. -This matrix is based on the current repository documentation, especially the [Operations Reference](reference/operations.md), [Platform Feature Flags](guides/feature-flags.md), [Script Structure Reference](reference/script-structure.md), and [Architecture Overview](getting-started/overview.md). +This matrix is based on the current repository documentation, especially the [Operations Reference](operations.md), [Platform Feature Flags](../concepts/feature-flags.md), [Script Structure Reference](script-structure.md), and [Architecture Overview](../concepts/architecture.md). > [!IMPORTANT] > This matrix is intentionally conservative. The repository explicitly documents custom platform development on **SPP 6.0 or later**, and it explicitly documents `ExecuteCommand` as available in **SPP 7.4 and later**. For several older capabilities, the exact introduction release is not called out in this repo, so entries marked `🆕` in the `SPP 6.0-7.3` column mean **available by the documented 6.0 baseline, but verify the exact minimum in the [One Identity documentation site](https://docs.oneidentity.com/)** before depending on the feature in older environments. @@ -58,8 +58,8 @@ This matrix is based on the current repository documentation, especially the [Op ## Related References -- [Operations Reference](reference/operations.md) -- [Platform Feature Flags](guides/feature-flags.md) -- [Script Structure Reference](reference/script-structure.md) +- [Operations Reference](operations.md) +- [Platform Feature Flags](../concepts/feature-flags.md) +- [Script Structure Reference](script-structure.md) - [System Import Libraries Reference](reference/imports.md) -- [Testing and Debugging](getting-started/testing-and-debugging.md) +- [Testing and Debugging](../guides/testing-and-debugging.md) diff --git a/docs/reference/imports.md b/docs/reference/imports.md index 4344d80..51e6bd1 100644 --- a/docs/reference/imports.md +++ b/docs/reference/imports.md @@ -141,6 +141,6 @@ If you need more than login/logout, you can import multiple libraries in the sam - [SSH Platforms Guide](../guides/ssh-platforms.md) - [Script Structure Reference](script-structure.md) -- [Your First SSH Script](../getting-started/your-first-ssh-script.md) +- [Your First SSH Script](../tutorials/your-first-ssh-script.md) - [SSH Key Management Guide](../guides/ssh-key-management.md) - [Account Discovery Guide](../guides/account-discovery.md) diff --git a/docs/reference/operations.md b/docs/reference/operations.md index 5061069..cec76a2 100644 --- a/docs/reference/operations.md +++ b/docs/reference/operations.md @@ -2,7 +2,7 @@ # Operations Reference -Operations are the named entry points in a custom platform script that SPP invokes when performing specific tasks. Each operation you include in your script tells SPP what your platform can do — SPP automatically derives [feature flags](../guides/feature-flags.md) from the operations present. +Operations are the named entry points in a custom platform script that SPP invokes when performing specific tasks. Each operation you include in your script tells SPP what your platform can do — SPP automatically derives [feature flags](../concepts/feature-flags.md) from the operations present. This page documents all 19 available operations across 8 categories. @@ -807,7 +807,7 @@ SPP automatically derives these feature flags when you upload a script. You neve | `TimeoutFl` | Any operation with `Timeout` parameter | | `CheckHostKeyFl` | Any operation with `CheckHostKey` parameter | -For a detailed guide on how feature flags affect platform behavior in the SPP UI, see [Feature Flags Guide](../guides/feature-flags.md). +For a detailed guide on how feature flags affect platform behavior in the SPP UI, see [Feature Flags Guide](../concepts/feature-flags.md). --- @@ -864,7 +864,7 @@ A comprehensive platform might implement all relevant operations: - [Script Structure](script-structure.md) — JSON structure overview - [Reserved Parameters](reserved-parameters.md) — complete auto-populated parameter reference -- [Feature Flags Guide](../guides/feature-flags.md) — how operations determine platform capabilities +- [Feature Flags Guide](../concepts/feature-flags.md) — how operations determine platform capabilities - [Commands Index](commands/index.md) — all available commands for implementing operations - [SSH Platforms Guide](../guides/ssh-platforms.md) — patterns for SSH-based implementations - [HTTP Platforms Guide](../guides/http-platforms.md) — patterns for HTTP-based implementations diff --git a/docs/reference/reserved-parameters.md b/docs/reference/reserved-parameters.md index df25d0f..ac4415e 100644 --- a/docs/reference/reserved-parameters.md +++ b/docs/reference/reserved-parameters.md @@ -52,7 +52,7 @@ When a script declares a reserved parameter name, SPP uses that name as a contra > **You don't configure these — SPP provides them from your Asset/Account/Profile setup.** -A few reserved names also influence UI exposure through [Feature Flags](../guides/feature-flags.md). For example, declaring access-key or instance-related parameters can cause the matching built-in asset fields to appear. +A few reserved names also influence UI exposure through [Feature Flags](../concepts/feature-flags.md). For example, declaring access-key or instance-related parameters can cause the matching built-in asset fields to appear. --- @@ -330,4 +330,4 @@ For more on defining non-reserved parameters, see [Custom Parameters](custom-par - [Operations](operations.md) — which operations use which reserved parameters - [Custom Parameters](custom-parameters.md) — defining your own non-reserved names -- [Feature Flags](../guides/feature-flags.md) — how reserved parameters and operations light up built-in behavior and UI +- [Feature Flags](../concepts/feature-flags.md) — how reserved parameters and operations light up built-in behavior and UI diff --git a/docs/reference/status-messages.md b/docs/reference/status-messages.md index e7df8d2..eedabf7 100644 --- a/docs/reference/status-messages.md +++ b/docs/reference/status-messages.md @@ -66,7 +66,7 @@ The command shape is the same for both transports. What changes is **where** you SSH and Telnet samples usually publish status immediately before or after `Connect`, `Send`, `Receive`, or `ExecuteCommand` steps. -From `SampleScripts/SSH/GenericLinux.json`: +From `samples/ssh/generic-linux/GenericLinux.json`: ```json { @@ -92,7 +92,7 @@ Typical SSH pattern: HTTP samples usually publish status around authentication requests, account lookups, and change requests. -From `SampleScripts/HTTP/CustomTwitter.json`: +From `samples/http/twitter/CustomTwitter.json`: ```json { @@ -307,7 +307,7 @@ Use this when the operation could not complete normally and the task should end - **Keep `Percent` meaningful.** Current samples commonly use milestone values such as `10`, `20`, `50`, `80`, `90`, and `95`. - **Match the phase with `Type`.** The same message family can appear under different operation phases, but `Type` should still describe the current step (`Connecting`, `Checking`, `Changing`, or `Discovering`). - **Follow existing parameter order.** For example, `AssetConnectFailedWithReasonAndAddress` is passed as `[ "%AssetName%", "%Address%", "%Exception%" ]` in the current samples. -- **Use extended logging when debugging.** `Status` shows the operator-friendly summary, while `extendedLogging=true` gives command-by-command trace detail. See [Testing and Debugging](../getting-started/testing-and-debugging.md). +- **Use extended logging when debugging.** `Status` shows the operator-friendly summary, while `extendedLogging=true` gives command-by-command trace detail. See [Testing and Debugging](../guides/testing-and-debugging.md). --- @@ -317,4 +317,4 @@ Use this when the operation could not complete normally and the task should end - [Error Handling Commands](commands/error-handling.md) - [Operations Reference](operations.md) - [Script Structure Reference](script-structure.md) -- [Testing and Debugging](../getting-started/testing-and-debugging.md) +- [Testing and Debugging](../guides/testing-and-debugging.md) diff --git a/docs/tutorials/your-first-form-script.md b/docs/tutorials/your-first-form-script.md index 4e9fb1b..8286dbb 100644 --- a/docs/tutorials/your-first-form-script.md +++ b/docs/tutorials/your-first-form-script.md @@ -445,5 +445,5 @@ For the complete development loop and log review, see [Development Workflow](dev - [Your First HTTP Script](your-first-http-script.md) — learn the API-first pattern for targets that expose REST endpoints. - [HTTP Platforms Guide](../guides/http-platforms.md) — broader HTTP scripting patterns and troubleshooting guidance. -- [`CustomFacebook.json`](../../SampleScripts/HTTP/CustomFacebook.json) — production sample showing login and password-change form submission. -- [`CustomTwitter.json`](../../SampleScripts/HTTP/CustomTwitter.json) — another form-based sample with different field names and redirects. +- [`CustomFacebook.json`](../../samples/http/facebook/CustomFacebook.json) — production sample showing login and password-change form submission. +- [`CustomTwitter.json`](../../samples/http/twitter/CustomTwitter.json) — another form-based sample with different field names and redirects. diff --git a/docs/tutorials/your-first-http-script.md b/docs/tutorials/your-first-http-script.md index 7422dda..0646f80 100644 --- a/docs/tutorials/your-first-http-script.md +++ b/docs/tutorials/your-first-http-script.md @@ -445,5 +445,5 @@ Once this minimal script works, you can build on it: - Add `ChangePassword` by following the HTTP patterns in the [HTTP Platforms Guide](../guides/http-platforms.md). - Implement Bearer token authentication for OAuth2-based APIs. -- Explore the real-world [`WordPressHttp.json`](../../SampleScripts/HTTP/WordPressHttp.json) sample. +- Explore the real-world [`WordPressHttp.json`](../../samples/http/wordpress/WordPressHttp.json) sample. - Read the [Operations Reference](../reference/operations.md). diff --git a/docs/tutorials/your-first-ssh-script.md b/docs/tutorials/your-first-ssh-script.md index 82d5293..148bb8c 100644 --- a/docs/tutorials/your-first-ssh-script.md +++ b/docs/tutorials/your-first-ssh-script.md @@ -249,7 +249,7 @@ Here is the full script with both operations in one file: } ``` -If you compare this with a production-ready sample such as [`GenericLinux.json`](../../SampleScripts/SSH/GenericLinux.json), you will notice that the sample adds reusable functions, richer validation, better error handling, and support for more SSH scenarios. Start with the minimal version here, then grow into those patterns later. +If you compare this with a production-ready sample such as [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json), you will notice that the sample adds reusable functions, richer validation, better error handling, and support for more SSH scenarios. Start with the minimal version here, then grow into those patterns later. ## Step 6: Validate and Upload @@ -315,5 +315,5 @@ Once this minimal script works, you are ready to extend it: - Add `ChangePassword` by following the SSH patterns in the [SSH platforms guide](../guides/ssh-platforms.md). - Add error handling with `Try` / `Catch` blocks. -- Explore the full [`GenericLinux.json`](../../SampleScripts/SSH/GenericLinux.json) sample for production-ready patterns such as imports, error handling, and `sudo`-based flows. +- Explore the full [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json) sample for production-ready patterns such as imports, error handling, and `sudo`-based flows. - Read the [Operations Reference](../reference/operations.md) to see the other operations you can implement. diff --git a/schema/README.md b/schema/README.md index 3f0b3ef..6b2960a 100644 --- a/schema/README.md +++ b/schema/README.md @@ -4,7 +4,7 @@ This schema adds editor-friendly **autocomplete**, **validation**, and **hover d ## Automatic setup in VS Code -This repository includes `.vscode/settings.json`, so anyone who clones the repo in VS Code gets automatic schema association for `SampleScripts/**/*.json`. +This repository includes `.vscode/settings.json`, so anyone who clones the repo in VS Code gets automatic schema association for `samples/**/*.json` and `templates/**/*.json`. ## Manual setup for other editors diff --git a/templates/README.md b/templates/README.md index 966bbf2..b9b75ea 100644 --- a/templates/README.md +++ b/templates/README.md @@ -1,41 +1,53 @@ -[← Sample Scripts](../README.md) +[← Repository README](../README.md) -# Pattern Templates +# Templates -These scripts are **pattern templates** — they illustrate recommended approaches -for common integration scenarios. They are _not_ tested against live targets and -should not be deployed without modification. +The files in `templates/` are **illustrative patterns and starter scaffolds**, not production-ready scripts. They are intentionally lightweight and are **not** presented as tested integrations for live targets. Use them to understand structure, common Safeguard operations, and recommended implementation approaches before you build your own platform. -Use them as a reference when building your own platform scripts, or copy a -minimal starter and expand from there. +## What is in this folder? -## Minimal Starters +- **Pattern templates** (`Pattern-*.json`) show recommended approaches for specific scenarios such as REST account discovery, JIT elevation, Linux file management, dependent systems, and API key rotation. +- **Minimal starters** (`Template*.json`) provide the smallest scaffold to begin from when you want a clean base and plan to add operations yourself. -| File | Description | -|------|-------------| -| `TemplateSshMinimal.json` | Bare-bones SSH script with CheckSystem only. Copy and expand. | -| `TemplateHttpMinimal.json` | Bare-bones HTTP script with CheckSystem only. Copy and expand. | +## How to use these templates -## Pattern Scripts +1. **Choose the closest starting point.** Pick a minimal starter for a blank scaffold, or a pattern template when one already matches your target workflow. +2. **Copy it to a new script.** Keep the original template unchanged and work in your own file. +3. **Customize it for your target.** Replace commands, endpoints, authentication, parsing, prompts, regex, parameters, and error handling. +4. **Validate and test it.** Review [script structure](../docs/reference/script-structure.md), [operations](../docs/reference/operations.md), and the [development workflow](../docs/guides/development-workflow.md). Then validate locally and test only against safe non-production assets with [testing and debugging guidance](../docs/guides/testing-and-debugging.md). +5. **Deploy after verification.** Import the finished script into SPP only after it behaves consistently in your environment. -| File | Demonstrates | -|------|-------------| -| `Pattern-GenericRestApiBasicAuth.json` | REST API integration using Basic authentication. | -| `Pattern-GenericRestApiBearerToken.json` | REST API integration using OAuth2 Bearer tokens. | -| `Pattern-GenericRestApiKeyRotation.json` | API key rotation workflow with rollback safety. | -| `Pattern-GenericHttpAccountDiscovery.json` | HTTP-based account discovery using paginated API responses. | -| `Pattern-GenericHttpJitElevation.json` | Just-In-Time privilege elevation via HTTP API. | -| `Pattern-GenericLinuxFull.json` | Full Linux SSH workflow: check, change, discover. | -| `Pattern-GenericLinuxDependentSystem.json` | Dependent system updates after a password change. | -| `Pattern-GenericLinuxFileManagement.json` | File-based credential management over SSH. | -| `Pattern-GenericLinuxServiceDiscovery.json` | Service and asset discovery on Linux hosts. | -| `Pattern-WindowsSshBasic.json` | Windows management via OpenSSH (non-WinRM). | +## Recommended documentation -## Functional Samples +- [Documentation hub](../docs/README.md) +- [Script structure](../docs/reference/script-structure.md) +- [Operations reference](../docs/reference/operations.md) +- [Development workflow](../docs/guides/development-workflow.md) +- [Testing and debugging](../docs/guides/testing-and-debugging.md) +- [SSH platforms](../docs/guides/ssh-platforms.md) +- [HTTP platforms](../docs/guides/http-platforms.md) -For production-ready scripts tested against real targets, see the parent -directories: +## Template catalog -- [SSH/](../SSH/) — Linux, AIX, macOS, and Windows-over-SSH samples -- [HTTP/](../HTTP/) — Facebook, OneLogin, Duo, and other HTTP-based platforms -- [Telnet/](../Telnet/) — TN3270 mainframe samples +| File | Type | Description | Key operations | Related docs | +| --- | --- | --- | --- | --- | +| `Pattern-GenericHttpAccountDiscovery.json` | Pattern | Paginated REST account discovery using `WriteDiscoveredAccount`. Adapt authentication, endpoint shape, and response parsing to your target API. | `CheckSystem`, `DiscoverAccounts` | [HTTP platforms](../docs/guides/http-platforms.md), [Account discovery](../docs/guides/account-discovery.md) | +| `Pattern-GenericHttpJitElevation.json` | Pattern | Idempotent JIT elevation and demotion over REST, modeled as add/remove group membership or equivalent privilege assignment. | `CheckSystem`, `ElevateAccount`, `DemoteAccount` | [HTTP platforms](../docs/guides/http-platforms.md), [JIT elevation](../docs/guides/jit-elevation.md) | +| `Pattern-GenericLinuxDependentSystem.json` | Pattern | SSH pattern for updating a dependent system after a primary credential change, using a caller-provided dependency command. | `CheckSystem`, `UpdateDependentSystem` | [SSH platforms](../docs/guides/ssh-platforms.md), [Dependent systems](../docs/guides/dependent-systems.md) | +| `Pattern-GenericLinuxFileManagement.json` | Pattern | SSH file-management pattern that checks, deploys, and verifies file content, including base64 decode and validation steps. | `CheckSystem`, `CheckFile`, `ChangeFile` | [SSH platforms](../docs/guides/ssh-platforms.md), [File management](../docs/guides/file-management.md) | +| `Pattern-GenericLinuxFull.json` | Pattern | Broad Linux SSH starting point that combines password management, SSH key management, account discovery, host key discovery, and enable/disable flows in one script. | `CheckSystem`, `CheckPassword`, `ChangePassword`, `CheckSshKey`, `ChangeSshKey`, `DiscoverAccounts`, `DiscoverSshHostKey`, `EnableAccount`, `DisableAccount` | [SSH platforms](../docs/guides/ssh-platforms.md), [SSH key management](../docs/guides/ssh-key-management.md), [Account discovery](../docs/guides/account-discovery.md) | +| `Pattern-GenericLinuxServiceDiscovery.json` | Pattern | SSH service-discovery pattern that queries `systemd` and emits results with `WriteDiscoveredService`. | `CheckSystem`, `DiscoverServices` | [SSH platforms](../docs/guides/ssh-platforms.md), [Operations reference](../docs/reference/operations.md) | +| `Pattern-GenericRestApiBasicAuth.json` | Pattern | REST API management with HTTP Basic authentication for connectivity, password validation, password change, and account discovery. | `CheckSystem`, `CheckPassword`, `ChangePassword`, `DiscoverAccounts` | [HTTP platforms](../docs/guides/http-platforms.md), [Account discovery](../docs/guides/account-discovery.md) | +| `Pattern-GenericRestApiBearerToken.json` | Pattern | REST API management with OAuth2 client credentials and bearer tokens for connectivity and password operations. | `CheckSystem`, `CheckPassword`, `ChangePassword` | [HTTP platforms](../docs/guides/http-platforms.md), [Your first HTTP script](../docs/tutorials/your-first-http-script.md) | +| `Pattern-GenericRestApiKeyRotation.json` | Pattern | REST API key validation and rotation workflow that you can adapt to your target system's key lifecycle and rollback needs. | `CheckSystem`, `CheckApiKey`, `ChangeApiKey` | [HTTP platforms](../docs/guides/http-platforms.md), [API key management](../docs/guides/api-key-management.md) | +| `Pattern-WindowsSshBasic.json` | Pattern | Windows password management over OpenSSH using PowerShell and `net user`, for environments that use SSH instead of WinRM. | `CheckSystem`, `CheckPassword`, `ChangePassword` | [SSH platforms](../docs/guides/ssh-platforms.md), [Operations reference](../docs/reference/operations.md) | +| `TemplateHttpMinimal.json` | Minimal starter | Smallest HTTP scaffold: a `CheckSystem` example that calls a health endpoint with a bearer token. Use it when you want to design your own HTTP workflow from scratch. | `CheckSystem` | [Your first HTTP script](../docs/tutorials/your-first-http-script.md), [Script structure](../docs/reference/script-structure.md) | +| `TemplateSshMinimal.json` | Minimal starter | Smallest SSH scaffold: connect, run a simple echo-style command, and disconnect in `CheckSystem`. Use it as a clean base for SSH-driven platforms. | `CheckSystem` | [Your first SSH script](../docs/tutorials/your-first-ssh-script.md), [Script structure](../docs/reference/script-structure.md) | + +## Need working examples instead? + +If you want fuller examples to study after you understand the patterns here, see: + +- [`samples/ssh/`](../samples/ssh/) +- [`samples/http/`](../samples/http/) +- [`samples/telnet/`](../samples/telnet/) From 4cfa8b4d505c601650dba632d93670d87d6496ca Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 18:33:38 -0600 Subject: [PATCH 04/13] Add companion READMEs for all samples - Add samples/README.md catalog with selection guidance - Add samples/ssh/README.md with SSH setup instructions - Add samples/http/README.md with HTTP setup instructions - Add samples/telnet/README.md with Telnet setup instructions - Add individual README.md for each of the 16 sample scripts - Each companion doc covers: purpose, operations, prerequisites, deployment --- samples/README.md | 49 +++++++++++++++ samples/http/README.md | 28 +++++++++ samples/http/facebook/README.md | 50 +++++++++++++++ samples/http/forgerock-openam/README.md | 54 ++++++++++++++++ samples/http/okta-discovery/README.md | 62 +++++++++++++++++++ samples/http/onelogin-jit/README.md | 58 +++++++++++++++++ samples/http/twitter/README.md | 50 +++++++++++++++ samples/http/wordpress/README.md | 52 ++++++++++++++++ samples/ssh/README.md | 28 +++++++++ samples/ssh/generic-linux-ssh-keys/README.md | 60 ++++++++++++++++++ samples/ssh/generic-linux-with-ad/README.md | 54 ++++++++++++++++ .../generic-linux-with-discovery/README.md | 56 +++++++++++++++++ samples/ssh/generic-linux/README.md | 54 ++++++++++++++++ samples/ssh/linux-app-text-config/README.md | 56 +++++++++++++++++ samples/ssh/linux-ssh-batch-mode/README.md | 53 ++++++++++++++++ .../ssh/restricted-authorized-key/README.md | 52 ++++++++++++++++ samples/ssh/vcenter-appliance/README.md | 53 ++++++++++++++++ samples/telnet/README.md | 22 +++++++ samples/telnet/cisco-ios/README.md | 53 ++++++++++++++++ samples/telnet/racf-tn3270/README.md | 54 ++++++++++++++++ 20 files changed, 998 insertions(+) create mode 100644 samples/README.md create mode 100644 samples/http/README.md create mode 100644 samples/http/facebook/README.md create mode 100644 samples/http/forgerock-openam/README.md create mode 100644 samples/http/okta-discovery/README.md create mode 100644 samples/http/onelogin-jit/README.md create mode 100644 samples/http/twitter/README.md create mode 100644 samples/http/wordpress/README.md create mode 100644 samples/ssh/README.md create mode 100644 samples/ssh/generic-linux-ssh-keys/README.md create mode 100644 samples/ssh/generic-linux-with-ad/README.md create mode 100644 samples/ssh/generic-linux-with-discovery/README.md create mode 100644 samples/ssh/generic-linux/README.md create mode 100644 samples/ssh/linux-app-text-config/README.md create mode 100644 samples/ssh/linux-ssh-batch-mode/README.md create mode 100644 samples/ssh/restricted-authorized-key/README.md create mode 100644 samples/ssh/vcenter-appliance/README.md create mode 100644 samples/telnet/README.md create mode 100644 samples/telnet/cisco-ios/README.md create mode 100644 samples/telnet/racf-tn3270/README.md diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000..cc6d4a4 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,49 @@ +# Sample Scripts + +Production-tested custom platform scripts you can deploy to SPP or study as reference implementations. Each sample includes a companion README explaining what it does, how to set it up, and how to deploy it. + +## Samples by Protocol + +| Protocol | Samples | Description | +| --- | --- | --- | +| [SSH](ssh/) | 8 samples | Linux, Unix, and appliance management over interactive or batch SSH. | +| [HTTP](http/) | 6 samples | REST APIs, OAuth2, browser-form workflows, and cloud services. | +| [Telnet](telnet/) | 2 samples | Network devices (Cisco IOS) and mainframes (IBM RACF TN3270). | + +## How to Use a Sample + +1. **Browse** the protocol directory that matches your target system. +2. **Read the README** in the sample's folder to understand prerequisites and setup. +3. **Download** the JSON file (or clone this repository). +4. **Upload** to SPP: + ```powershell + Import-SafeguardCustomPlatformScript -FilePath .\SampleScript.json + ``` +5. **Create an asset** using the custom platform and configure accounts. +6. **Test** with `ExtendedLogging` before production use. + +## Samples vs. Templates + +| | Samples (`samples/`) | Templates (`templates/`) | +| --- | --- | --- | +| **Tested** | ✅ Tested against real target systems | ❌ Not tested against live targets | +| **Purpose** | Deploy to production (with customization) | Learn patterns and start new scripts | +| **Completeness** | Full implementations with error handling | Illustrative — may omit edge cases | + +If you want a starting point for a new platform rather than a deployable sample, see [Templates](../templates/). + +## Quick Reference: Which Sample Do I Need? + +| I need to... | Start here | +| --- | --- | +| Manage Linux local accounts (SSH) | [generic-linux](ssh/generic-linux/) | +| Add account discovery to Linux | [generic-linux-with-discovery](ssh/generic-linux-with-discovery/) | +| Manage SSH keys on Linux | [generic-linux-ssh-keys](ssh/generic-linux-ssh-keys/) | +| Use SSH batch mode (no interactive shell) | [linux-ssh-batch-mode](ssh/linux-ssh-batch-mode/) | +| Manage a REST API with Basic Auth | [wordpress](http/wordpress/) | +| Manage a REST API with tokens (OAuth2) | [forgerock-openam](http/forgerock-openam/) | +| Discover accounts via REST API | [okta-discovery](http/okta-discovery/) | +| Implement JIT privilege elevation | [onelogin-jit](http/onelogin-jit/) | +| Handle browser-form login flows | [facebook](http/facebook/) or [twitter](http/twitter/) | +| Manage a Cisco IOS device (Telnet) | [cisco-ios](telnet/cisco-ios/) | +| Manage an IBM mainframe (TN3270) | [racf-tn3270](telnet/racf-tn3270/) | diff --git a/samples/http/README.md b/samples/http/README.md new file mode 100644 index 0000000..c0aa29b --- /dev/null +++ b/samples/http/README.md @@ -0,0 +1,28 @@ +# HTTP Samples + +Tested custom platform scripts for managing systems over HTTP/REST APIs. These samples cover REST APIs with various authentication methods, browser-form login workflows, and cloud service integrations. + +| Sample | Complexity | Target System | +| --- | --- | --- | +| [facebook](facebook/) | ⭐⭐⭐ | Browser-form credential management (Facebook-style) | +| [twitter](twitter/) | ⭐⭐⭐ | Browser-form login with challenge handling (Twitter-style) | +| [forgerock-openam](forgerock-openam/) | ⭐⭐ | ForgeRock AM 7.5 REST API | +| [okta-discovery](okta-discovery/) | ⭐⭐⭐ | Okta with account discovery and group membership | +| [onelogin-jit](onelogin-jit/) | ⭐⭐⭐ | OneLogin JIT elevation and account activation | +| [wordpress](wordpress/) | ⭐⭐ | WordPress REST API with Basic Auth | + +## Choosing a Sample + +- **Simple REST API with Basic Auth?** Start with [wordpress](wordpress/). +- **Token-based (OAuth2/Bearer)?** Look at [forgerock-openam](forgerock-openam/). +- **Need account discovery?** See [okta-discovery](okta-discovery/). +- **JIT elevation workflow?** Try [onelogin-jit](onelogin-jit/). +- **Browser-form login (not a REST API)?** See [facebook](facebook/) or [twitter](twitter/). + +## Related Docs + +- [HTTP Platforms Guide](../../docs/guides/http-platforms.md) — patterns and best practices for HTTP platforms +- [Commands: Request](../../docs/reference/commands/request.md) — HTTP request command reference +- [Commands: HTTP Auth](../../docs/reference/commands/http-auth.md) — authentication methods +- [Commands: Forms](../../docs/reference/commands/forms.md) — HTML form extraction and submission +- [Commands: JSON](../../docs/reference/commands/json.md) — JSON response parsing diff --git a/samples/http/facebook/README.md b/samples/http/facebook/README.md new file mode 100644 index 0000000..b362bc0 --- /dev/null +++ b/samples/http/facebook/README.md @@ -0,0 +1,50 @@ +# Facebook Password Management (HTTP Form) + +This sample validates and changes Facebook account passwords by replaying Facebook's browser-based login and security forms. It is a form-scraping example for sites that do not expose a supported administrative API. + +## Target System + +Facebook user accounts accessed through the public web interface. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckPassword` | Opens the Facebook login form, submits the managed account credentials, and succeeds only when the redirect pattern indicates a normal authenticated session. | +| `ChangePassword` | Signs in with the current password, opens the security/password page, submits the password-change form, logs out, and verifies the new password by logging in again. | + +## Prerequisites + +- SPP 6.0 or later +- Outbound HTTPS access from SPP to `https://www.facebook.com` +- The managed account must use password-based sign-in only; checkpoint, login approvals, MFA prompts, or CAPTCHA must be disabled +- Managed account username/email and current password; no separate service account is required + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./CustomFacebook.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure the managed account(s); no separate service account is required +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script fetches the no-script Facebook login page, extracts the login form, and posts the supplied credentials. For password change, it navigates to the security settings page, fills in the current, new, and confirmation password fields, submits the form, logs out, and then signs in again with the new password to confirm the rotation succeeded. + +## Parameters + +- `Timeout`: Optional operation timeout in seconds. Default: `30`. +- `AssetName`: Friendly name used in status messages. Default: `Facebook`. + +## Limitations + +- This sample depends on Facebook's current HTML form fields and redirect behavior, so UI changes can break it without warning. +- It does not handle checkpoint flows, login approvals, MFA, CAPTCHA, or other interactive challenges. +- `ChangePassword` returns `false` if the current and new password are identical. + +## Related + +- [HTTP platform patterns](../../../docs/guides/http-platforms.md) +- [Forms command reference](../../../docs/reference/commands/forms.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/http/forgerock-openam/README.md b/samples/http/forgerock-openam/README.md new file mode 100644 index 0000000..82717b8 --- /dev/null +++ b/samples/http/forgerock-openam/README.md @@ -0,0 +1,54 @@ +# ForgeRock AM Password Management (HTTP API) + +This sample manages ForgeRock AM/OpenAM users through the REST API for a specific realm. It uses header-based authentication to validate credentials and to set a user's `userpassword` attribute. + +## Target System + +ForgeRock AM / OpenAM user accounts in a target realm. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Authenticates the service account against the realm's `/authenticate` endpoint to verify connectivity and credentials. | +| `CheckPassword` | Authenticates the managed account against the same realm endpoint to verify the current password. | +| `ChangePassword` | Authenticates with the service account and updates the target user's `userpassword` through the realm's user API. | + +## Prerequisites + +- SPP 6.0 or later +- ForgeRock AM/OpenAM 7.5 or later recommended; this sample was tested with AM 7.5 +- Network access from SPP to the AM endpoint and realm +- A service account with permission to authenticate and update users in the target realm + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./Forgerock_OpenAM.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s), including the target `Realm` +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script builds an HTTP or HTTPS base address from `Address`, `Port`, and `UseSsl`. `CheckSystem` and `CheckPassword` call `openam/json/realms/%Realm%/authenticate` with `Accept-API-Version`, `X-OpenAM-Username`, and `X-OpenAM-Password` headers. `ChangePassword` first authenticates the service account, then sends a `PUT` to `openam/json/realms/%Realm%/users/%Username%` with a JSON body containing the new `userpassword` value. + +## Parameters + +- `Realm`: Required ForgeRock realm name used in all API paths. +- `Address`: AM host name or address. +- `Port`: Optional custom port to append to the address. +- `UseSsl`: Uses HTTPS when `true`; HTTP when `false`. +- `SkipServerCertValidation`: Ignores certificate validation errors when enabled. + +## Limitations + +- The sample updates only the `userpassword` field in the specified realm. +- It relies on the ForgeRock AM API paths and headers shown in the sample and may need adjustment for different deployments or custom paths. +- Password policy failures are surfaced only through the returned HTTP status/error. + +## Related + +- [HTTP platform patterns](../../../docs/guides/http-platforms.md) +- [Request command reference](../../../docs/reference/commands/request.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/http/okta-discovery/README.md b/samples/http/okta-discovery/README.md new file mode 100644 index 0000000..92d4d16 --- /dev/null +++ b/samples/http/okta-discovery/README.md @@ -0,0 +1,62 @@ +# Okta Discovery and Group Restore (HTTP API) + +This sample combines password validation, password reset, account discovery, and group-membership restore/suspend workflows for Okta. It uses an Okta API token for administrative operations and the `/authn` endpoint for end-user password checks. + +## Target System + +Okta users and group memberships in an Okta tenant. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Looks up a known user with the administrative API token to confirm the tenant is reachable and the token works. | +| `CheckPassword` | Calls `api/v1/authn` with the managed account credentials to verify the current password. | +| `ChangePassword` | Resolves the target user ID and updates the password through the Okta Users API. | +| `EnableAccount` | Adds the user back to the configured Okta groups selected by the `Group1`-`Group5` rules. | +| `DisableAccount` | Removes the user from the configured Okta groups selected by the `Group1`-`Group5` rules. | +| `DiscoverAccounts` | Pages through Okta users, reads each user's group memberships, and writes discovered accounts with group data back to Safeguard. | + +## Prerequisites + +- SPP 6.0 or later +- Network access from SPP to the Okta tenant URL configured in `Address` +- An Okta API token with rights to read users and groups, change passwords, and add/remove users from groups +- `FuncUsername` must be the login name of an existing Okta user for `CheckSystem`; `FuncPassword` is the Okta API token used as the `SSWS` authorization value + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./Okta_WithDiscoveryAndGroupMembershipRestore.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account, managed account(s), and any group restore rules you want to use +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +Administrative operations build an `Authorization: SSWS %FuncPassword%` header and call the Okta Users and Groups APIs. Password checks use `api/v1/authn` with the managed account username and password. `EnableAccount` and `DisableAccount` evaluate up to five group rules, where each rule can target `` accounts or accounts whose usernames contain any comma-separated substring, then add or remove memberships with `api/v1/groups/%GroupId%/users/%UserId%`. Discovery calls `api/v1/users`, follows Okta pagination via the `Link` header, fetches each user's groups, and writes discovered accounts plus group memberships. + +## Parameters + +- `FuncPassword`: Okta API token used for all administrative API calls. +- `FuncUsername`: Existing Okta username used only by `CheckSystem`. +- `Group1Name`-`Group5Name`: Optional Okta groups to add back on restore or remove on suspend. +- `Group1Members`-`Group5Members`: `` or comma-separated username fragments used to decide which accounts map to each group. +- `SearchCriteria`: Optional raw Okta `search=` expression used during discovery. +- `ResultsPageLimit`: Users fetched per discovery request. Default: `25`. +- `UseSsl` / `SkipServerCertValidation`: Control HTTPS usage and certificate validation. + +## Limitations + +- The sample was tested with ACTIVE Okta users. +- `EnableAccount` and `DisableAccount` change group membership only; they do not activate or deactivate the Okta user object itself. +- Discovery is capped by Okta's page size behavior; the script forces any requested page size above `200` back to `200`. +- Group matching is substring-based and limited to five configured group rules, so choose member patterns carefully to avoid unintended matches. +- Okta discovery does not list deactivated users by default. + +## Related + +- [HTTP platform patterns](../../../docs/guides/http-platforms.md) +- [Account discovery guide](../../../docs/guides/account-discovery.md) +- [Operations reference](../../../docs/reference/operations.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/http/onelogin-jit/README.md b/samples/http/onelogin-jit/README.md new file mode 100644 index 0000000..b4d5063 --- /dev/null +++ b/samples/http/onelogin-jit/README.md @@ -0,0 +1,58 @@ +# OneLogin JIT Account Lifecycle and Role Elevation + +This sample is a JIT-focused add-on for OneLogin environments already managed through a separate Generic REST connector. It validates OAuth client credentials, enables or disables users, and elevates or demotes users by assigning or removing OneLogin roles. + +## Target System + +OneLogin users and role assignments used in Safeguard JIT access workflows. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Requests an OAuth access token with the configured client credentials, then revokes it to verify connectivity and credentials. | +| `ChangePassword` | Placeholder only; logs that password changes are not supported and returns `false`. | +| `EnableAccount` | Looks up the user by username and sets the OneLogin status to enabled. | +| `DisableAccount` | Looks up the user by username and sets the OneLogin status to disabled. | +| `ElevateAccount` | Resolves each requested OneLogin role in `PrivilegeGroupMembership`, assigns the user to those roles, and polls until the assignments are visible. | +| `DemoteAccount` | Resolves each requested OneLogin role in `PrivilegeGroupMembership`, removes the user from those roles, and polls until the removals are visible. | + +## Prerequisites + +- SPP 6.0 or later +- Network access from SPP to the OneLogin API endpoint in `Address` +- A separate OneLogin Generic REST connector already managing the base asset, account, and entitlement inventory +- OneLogin OAuth client credentials with rights to manage users and roles; configure the client ID as `FuncUsername` and the client secret as `FuncPassword` + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./OneLogin_GRC_JIT_addon.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account, managed account(s), and JIT role mappings for `PrivilegeGroupMembership` +5. Test with `Test-SafeguardAsset -ExtendedLogging`, then exercise the access-request workflows that call `EnableAccount`, `DisableAccount`, `ElevateAccount`, and `DemoteAccount` + +## How It Works + +The script authenticates with `auth/oauth2/v2/token` using client credentials and stores the returned bearer token. For account lifecycle operations it looks up the OneLogin user by username and sends a `PUT` to `api/2/users/%UserId%` with the desired status. For role elevation and demotion it resolves each role name to a role ID, adds or removes the user with `api/2/roles/%RoleId%/users`, and repeatedly checks role membership with `api/2/roles/%RoleId%/users?name=%Username%` until the change is visible. At the end of each run it revokes the access token with `auth/oauth2/revoke`. + +## Parameters + +- `PrivilegeGroupMembership`: Array of OneLogin role names to grant during elevate and remove during demote. +- `RetryIntervalSeconds`: Delay between role-membership verification polls. Default: `5`. +- `HttpProxyUri`, `HttpProxyPort`, `HttpProxyUserName`, `HttpProxyPassword`: Optional outbound proxy settings used on API calls. +- `SkipServerCertValidation`: Controls TLS certificate validation. + +## Limitations + +- `ChangePassword` is intentionally unsupported; OneLogin accounts in this design are expected to use TOTP or other non-password flows. +- The role-verification loop has no maximum retry count, so a stuck downstream provisioning problem can leave an elevate or demote task pending indefinitely. +- If one configured role cannot be found or updated, the script logs the failure and continues with the next role. Review extended logs carefully when multiple roles are requested. +- `ElevateAccount` requires the user to already be active; inactive users are rejected before any role assignment is attempted. + +## Related + +- [JIT elevation guide](../../../docs/guides/jit-elevation.md) +- [HTTP platform patterns](../../../docs/guides/http-platforms.md) +- [Reserved parameters reference](../../../docs/reference/reserved-parameters.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/http/twitter/README.md b/samples/http/twitter/README.md new file mode 100644 index 0000000..c4737a6 --- /dev/null +++ b/samples/http/twitter/README.md @@ -0,0 +1,50 @@ +# Twitter Password Management (HTTP Form) + +This sample validates and changes Twitter account passwords by walking the site's login and settings forms over HTTP. It also detects several common challenge states that prevent unattended password management. + +## Target System + +Twitter/X user accounts accessed through the web interface. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckPassword` | Loads the login page, submits the managed account credentials, and returns success only when the login completes without an error redirect. | +| `ChangePassword` | Signs in, detects login-challenge or locked-account conditions, posts the password-change form from the settings page, and checks for the expected confirmation redirect. | + +## Prerequisites + +- SPP 6.0 or later +- Outbound HTTPS access from SPP to `https://twitter.com` +- The managed account must support direct username/password sign-in without extra verification prompts +- Managed account username and current password; no separate service account is required + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./CustomTwitter.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure the managed account(s); no separate service account is required +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script first resets internal flags for login challenge and account-locked detection. It then fetches the Twitter login form, submits the current credentials, and inspects redirect targets to detect login errors, verification challenges, or locked accounts. For password changes it opens `settings/password`, fills the current and new password fields, posts the update, and treats the password-reset confirmation redirect as success. + +## Parameters + +- `Timeout`: Optional operation timeout in seconds. Default: `30`. +- `AssetName`: Friendly name used in status messages. Default: `Twitter`. + +## Limitations + +- This sample depends on Twitter's current login and settings pages, so HTML or redirect-flow changes can break it. +- Login challenges, login verification, locked accounts, and access-restriction flows are detected and reported as failures rather than being automated. +- The sample assumes password-based authentication only. + +## Related + +- [HTTP platform patterns](../../../docs/guides/http-platforms.md) +- [Forms command reference](../../../docs/reference/commands/forms.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/http/wordpress/README.md b/samples/http/wordpress/README.md new file mode 100644 index 0000000..65c1586 --- /dev/null +++ b/samples/http/wordpress/README.md @@ -0,0 +1,52 @@ +# WordPress Password Management (Basic Auth API) + +This sample manages WordPress users through the REST API using HTTP Basic authentication. It is a straightforward API example for environments where the site exposes authenticated REST endpoints for user administration. + +## Target System + +WordPress user accounts exposed through the WordPress REST API. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Uses the service account to call the REST API settings endpoint and verify connectivity and privileges. | +| `CheckPassword` | Uses the managed account credentials with Basic auth to call `/users/me/` and confirm the password works. | +| `ChangePassword` | Uses the service account to enumerate users, locate the target user, and post a new password to that user's REST endpoint. | + +## Prerequisites + +- SPP 6.0 or later +- A WordPress site reachable from SPP with the REST API enabled +- The JSON Basic Authentication plugin (or equivalent Basic-auth support) installed on the site +- A service account with permission to read settings and update WordPress users; use HTTPS because Basic auth sends credentials on every request + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./WordPressHttp.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account, managed account(s), and the `APIURL` path for the site's REST API +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script chooses HTTP or HTTPS from `UseSsl`, builds a request object, and attaches Basic authentication. `CheckSystem` calls `%APIURL%/settings`, while `CheckPassword` calls `%APIURL%/users/me/` with the managed account's credentials. For password changes it first calls `%APIURL%/users?per_page=100`, finds the matching user, then posts a JSON body containing the new password to that user's endpoint and interprets the returned HTTP status. + +## Parameters + +- `APIURL`: Required REST path below the site root, such as `wp-json/wp/v2`. +- `UseSsl`: Uses HTTPS when `true`; strongly recommended for production. +- `SkipServerCertValidation`: Ignores certificate validation errors when enabled. + +## Limitations + +- This sample requires Basic-auth support on the WordPress REST API; stock WordPress does not enable this by default. +- `ChangePassword` only searches the first `100` users because the sample uses `per_page=100`. +- User lookup in `ChangePassword` compares the configured account name to the `name` field returned by the API, so the Safeguard account name must match that value. + +## Related + +- [HTTP platform patterns](../../../docs/guides/http-platforms.md) +- [HTTP auth reference](../../../docs/reference/commands/http-auth.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/ssh/README.md b/samples/ssh/README.md new file mode 100644 index 0000000..af9f150 --- /dev/null +++ b/samples/ssh/README.md @@ -0,0 +1,28 @@ +# SSH Samples + +Tested custom platform scripts for managing systems over SSH. These samples cover interactive expect-style patterns and batch-mode execution. + +| Sample | Complexity | Target System | +| --- | --- | --- | +| [generic-linux](generic-linux/) | ⭐⭐ | Standard Linux (local accounts, interactive SSH) | +| [generic-linux-with-ad](generic-linux-with-ad/) | ⭐⭐ | Linux with AD domain-qualified service account | +| [generic-linux-with-discovery](generic-linux-with-discovery/) | ⭐⭐⭐ | Linux with local account discovery | +| [generic-linux-ssh-keys](generic-linux-ssh-keys/) | ⭐⭐⭐ | Linux with SSH authorized key lifecycle | +| [linux-app-text-config](linux-app-text-config/) | ⭐⭐⭐ | Application passwords in text config files | +| [linux-ssh-batch-mode](linux-ssh-batch-mode/) | ⭐⭐ | Linux using batch-mode SSH (no interactive shell) | +| [restricted-authorized-key](restricted-authorized-key/) | ⭐⭐⭐ | Linux with restricted key + passwordless sudo | +| [vcenter-appliance](vcenter-appliance/) | ⭐⭐⭐ | VMware vCenter Server Appliance | + +## Choosing a Sample + +- **New to SSH platforms?** Start with [generic-linux](generic-linux/) — it's the baseline. +- **Need account discovery?** Look at [generic-linux-with-discovery](generic-linux-with-discovery/). +- **Managing SSH keys?** See [generic-linux-ssh-keys](generic-linux-ssh-keys/). +- **Prefer batch commands over interactive shells?** Try [linux-ssh-batch-mode](linux-ssh-batch-mode/). + +## Related Docs + +- [SSH Platforms Guide](../../docs/guides/ssh-platforms.md) — patterns and best practices for SSH platforms +- [Commands: Connect/Disconnect](../../docs/reference/commands/connect.md) — connection management reference +- [Commands: Send/Receive](../../docs/reference/commands/send-receive.md) — interactive terminal commands +- [Commands: ExecuteCommand](../../docs/reference/commands/execute-command.md) — batch-mode command execution diff --git a/samples/ssh/generic-linux-ssh-keys/README.md b/samples/ssh/generic-linux-ssh-keys/README.md new file mode 100644 index 0000000..ab04a6c --- /dev/null +++ b/samples/ssh/generic-linux-ssh-keys/README.md @@ -0,0 +1,60 @@ +# Generic Linux with SSH Key Support + +This sample adds SSH authorized-key management to the generic Linux password-management flow. It can discover existing keys, check whether a specific key is installed, add a new key, optionally test the matching private key, and remove an old key. + +## Target System + +A Linux or Unix host that uses OpenSSH-style `AuthorizedKeysFile` paths for managed accounts. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Verifies the service account can log in and access the required privileged data. | +| `CheckPassword` | Validates a managed-account password by comparing it to the `/etc/shadow` hash. | +| `ChangePassword` | Changes the managed-account password with interactive `passwd`. | +| `CheckSshKey` | Resolves the target authorized-key files and reports whether `OldSshKey` is already installed. | +| `ChangeSshKey` | Resolves the key files, appends `NewSshKey`, optionally tests `NewSshPrivateKey`, and removes `OldSshKey`. | +| `DiscoverAuthorizedKeys` | Reads the resolved authorized-key files, parses valid key lines, and emits discovered SSH keys. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for the target asset. | + +## Prerequisites + +- SPP version 6.0 or later +- A Linux/OpenSSH host reachable over SSH +- A service account that can run `sshd -T -C`, `id`, and the required file-management commands (`mkdir`, `touch`, `cp`, `cat`, `tee`, `mv`, `chown`, `chmod`) through `sudo` or the configured `DelegationPrefix` +- Managed accounts that store SSH keys in standard OpenSSH authorized-keys files + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./GenericLinuxWithSSHKeySupport.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script starts with the same interactive SSH login and shell initialization as the generic Linux sample. For SSH key workflows, it discovers the managed account's UID, GID, home directory, and effective `AuthorizedKeysFile` templates by running `sshd -T -C ...`, with a fallback to `%h/.ssh/authorized_keys %h/.ssh/authorized_keys2`. It resolves those templates to concrete paths, reads and parses valid OpenSSH key lines, and uses that data for check and discovery operations. During `ChangeSshKey`, it backs up the primary key file, appends the new public key, restores ownership and permissions, optionally tests login with `NewSshPrivateKey`, and then removes the old key from whichever file currently contains it. + +## Parameters + +- `OldSshKey` - Existing public key to verify or remove +- `NewSshKey` - New public key to install +- `NewSshPrivateKey` - Optional private key used to test the newly installed public key +- `DelegationPrefix` - Privilege-elevation command used for shadow access and file updates +- `UserKey` - Optional SSH private key for the service account login + +## Limitations + +- Does not implement a standalone `RemoveAuthorizedKey` operation +- Installs new keys into the first resolved authorized-keys path +- Key parsing is limited to the key types explicitly handled in `ParseKey` +- Removal is line-oriented text processing and may remove duplicate matching entries +- Assumes `sshd -T -C` is available on the target host + +## Related + +- [SSH Key Management Guide](../../../docs/guides/ssh-key-management.md) +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [SPP Compatibility Matrix](../../../docs/reference/compatibility.md) diff --git a/samples/ssh/generic-linux-with-ad/README.md b/samples/ssh/generic-linux-with-ad/README.md new file mode 100644 index 0000000..dd8a0ac --- /dev/null +++ b/samples/ssh/generic-linux-with-ad/README.md @@ -0,0 +1,54 @@ +# Generic Linux with Active Directory Service Account + +This sample is the generic Linux SSH password-management script with an optional Active Directory-style login name for the service account. It appends `@domain` during SSH login, then uses the same local `/etc/passwd` and `/etc/shadow` workflow to validate and change managed-account passwords. + +## Target System + +A Linux host where the service account may authenticate as `user@domain`, while password management still targets local accounts on the host. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Logs in with the service account, optionally as `user@domain`, and verifies delegated access to the required shadow entry. | +| `CheckPassword` | Confirms the managed account exists locally and compares the supplied password to the `/etc/shadow` hash. | +| `ChangePassword` | Runs `passwd` for the managed account and handles the interactive password-change prompts. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for asset trust configuration. | + +## Prerequisites + +- SPP version 6.0 or later +- A Linux host reachable over SSH +- If you use `FuncUserDomain`, the target must accept SSH logins in `user@domain` form +- A service account that can use `sudo` (or another `DelegationPrefix`) to read `/etc/shadow` and run `passwd` + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./GenericLinuxWithAD.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +`LoginSsh` optionally rewrites the SSH login to `%FuncUserName%@%FuncUserDomain%` before connecting. After login, the script sets a consistent shell environment and uses delegated commands to inspect `/etc/shadow`. Password validation and password change follow the same pattern as the generic Linux sample: check the local account in `/etc/passwd`, read or update the local password data, and react to `sudo` or `passwd` prompts as needed. + +## Parameters + +- `FuncUserDomain` - Optional domain suffix appended to the service account at SSH login time +- `DelegationPrefix` - Privilege-elevation command, typically `sudo` +- `RequestTerminal` - Keeps the connection in interactive shell mode for prompt-driven commands +- `UserKey` - Optional SSH private key for service-account authentication + +## Limitations + +- The AD-specific logic only affects the SSH login name; managed-account validation still relies on local `/etc/passwd` and `/etc/shadow` +- Assumes Linux `passwd` prompts and shadow-file format match the sample regexes +- Does not discover or manage directory accounts directly + +## Related + +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [Custom Parameters Reference](../../../docs/reference/custom-parameters.md) +- [SPP Compatibility Matrix](../../../docs/reference/compatibility.md) diff --git a/samples/ssh/generic-linux-with-discovery/README.md b/samples/ssh/generic-linux-with-discovery/README.md new file mode 100644 index 0000000..f9d0494 --- /dev/null +++ b/samples/ssh/generic-linux-with-discovery/README.md @@ -0,0 +1,56 @@ +# Generic Linux with Account Discovery + +This sample extends the generic Linux SSH password-management flow with account discovery. In addition to checking and changing passwords, it enumerates Unix accounts and group memberships and reports them back to Safeguard. + +## Target System + +A generic Linux host with local accounts in `/etc/passwd`, password hashes in `/etc/shadow`, and standard Unix identity commands such as `id` and `awk`. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Verifies the service account can log in and access the required privileged data. | +| `CheckPassword` | Validates a managed-account password by comparing it to the `/etc/shadow` entry. | +| `ChangePassword` | Changes the managed-account password through the interactive `passwd` command. | +| `DiscoverAccounts` | Enumerates local accounts, UIDs, primary GIDs, and group memberships, then emits `WriteDiscoveredAccount` records. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for trust-on-first-use style onboarding. | + +## Prerequisites + +- SPP version 6.0 or later +- A Linux host reachable over SSH +- A service account with enough privilege to read `/etc/shadow`, inspect `/etc/passwd`, and run the discovery pipeline commands (`grep`, `wc`, `cut`, `tr`, `id`, and `awk`) +- An account-discovery job in SPP if you want to use `DiscoverAccounts` + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./GenericLinuxWithDiscovery.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The password-management operations follow the same interactive SSH pattern as the generic Linux sample. `DiscoverAccounts` logs in, sets a predictable shell environment, counts candidate accounts in `/etc/passwd`, then runs a shell pipeline that combines `/etc/passwd` data with `id` output to collect each user's UID, primary group, and supplemental groups. It parses the resulting lines with regex, writes one discovered account per match, and can still return partial results if the command times out after producing some data. + +## Parameters + +- `DiscoveryQuery` - Required reserved parameter that enables account discovery in SPP +- `DelegationPrefix` - Privilege-elevation command used during password and discovery operations +- `UserKey` - Optional SSH private key for the service account +- `RequestTerminal` - Keeps the connection in interactive shell mode + +## Limitations + +- Discovers local `/etc/passwd` accounts only +- The discovery operation can return partial results when the remote command times out +- `FuncUserDomain` is declared for discovery but is not used by the sample's discovery login flow +- Password validation and change still depend on `/etc/shadow` access and Linux `passwd` prompts + +## Related + +- [Account Discovery Guide](../../../docs/guides/account-discovery.md) +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [Operations Reference](../../../docs/reference/operations.md) diff --git a/samples/ssh/generic-linux/README.md b/samples/ssh/generic-linux/README.md new file mode 100644 index 0000000..b57cfb0 --- /dev/null +++ b/samples/ssh/generic-linux/README.md @@ -0,0 +1,54 @@ +# Generic Linux Password Management + +This sample manages local Linux account passwords over SSH using an interactive shell. It verifies service-account access, validates managed-account passwords against `/etc/shadow`, and changes passwords with `passwd`. + +## Target System + +A generic Linux host with local accounts in `/etc/passwd` and password hashes in `/etc/shadow`. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Logs in with the service account, initializes the shell, and verifies that the service account can read the required shadow entry through the configured delegation command. | +| `CheckPassword` | Confirms the managed account exists, reads its `/etc/shadow` entry, and compares the supplied password to the stored hash. | +| `ChangePassword` | Runs `passwd` for the target account, handles interactive prompts, and submits the new password. | +| `DiscoverSshHostKey` | Retrieves the SSH host key so it can be stored on the asset. | + +## Prerequisites + +- SPP version 6.0 or later +- A Linux host reachable over SSH +- A service account that can log in over SSH and use `sudo` (or another `DelegationPrefix`) to read `/etc/shadow` and run `passwd` +- If `sudo` prompts for a password, the service account password must be supplied; optional SSH key login is supported through `UserKey` + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./GenericLinux.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script connects over SSH, flushes the login banner, and sets a predictable shell environment with a known `sudo` prompt. `CheckSystem` verifies delegation by looking up the service account in `/etc/shadow`. `CheckPassword` checks that the managed account exists in `/etc/passwd`, retrieves the shadow entry through the delegation command, and uses `CompareShadowHash` to validate the supplied password. `ChangePassword` drives the interactive `passwd` flow, handling optional `sudo` and current-password prompts before sending the new password twice. + +## Parameters + +- `DelegationPrefix` - Command used for privilege elevation, typically `sudo` +- `RequestTerminal` - Controls whether SSH requests a PTY; defaults to `true` for interactive flows +- `UserKey` - Optional SSH private key for the service account login + +## Limitations + +- Designed for local Unix accounts backed by `/etc/passwd` and `/etc/shadow` +- Assumes interactive `passwd` prompts match the regexes in the sample +- Password validation requires enough privilege to read `/etc/shadow` +- Expired-password or forced-password-change login banners cause login validation to fail + +## Related + +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [Operations Reference](../../../docs/reference/operations.md) +- [SPP Compatibility Matrix](../../../docs/reference/compatibility.md) diff --git a/samples/ssh/linux-app-text-config/README.md b/samples/ssh/linux-app-text-config/README.md new file mode 100644 index 0000000..4b72e5a --- /dev/null +++ b/samples/ssh/linux-app-text-config/README.md @@ -0,0 +1,56 @@ +# Linux Application Password in a Text Configuration File + +This sample changes an application password stored in a plain-text configuration file on Linux over SSH. It is intended for simple legacy applications where password rotation means replacing a single `prefix + password` line in a file. + +## Target System + +A Linux host that stores an application password in a text file that can be updated with shell tools such as `sed`. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Verifies the service account can log in and access the target host with the required delegation command. | +| `CheckPassword` | Present in the sample, but currently returns `false` immediately and is not implemented. | +| `ChangePassword` | Rewrites the configured file by replacing the line that starts with `ApplicationPasswordPrefix` and moving the updated file into place. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for the asset. | + +## Prerequisites + +- SPP version 6.0 or later +- A Linux host reachable over SSH +- A service account that can use `sudo` (or another `DelegationPrefix`) to edit the application configuration file +- The path, filename, and password-line prefix for the target configuration file + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./LinuxApplicationTextConfig.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +After connecting over SSH, the script prepares a predictable shell prompt and `sudo` prompt. `ChangePassword` builds the full target path from `ApplicationFilePath` and `ApplicationFileName`, then runs a `sed` replacement that rewrites the line beginning with `ApplicationPasswordPrefix` into a `.new` file and renames that file back over the original. The flow is intentionally simple: it does not parse an application-specific format, it just performs a prefix-based text replacement. `CheckPassword` logs that it is not implemented and exits before performing any validation. + +## Parameters + +- `ApplicationFilePath` - Directory path that contains the configuration file +- `ApplicationFileName` - Name of the file to update +- `ApplicationPasswordPrefix` - Leading text that identifies the password line to replace +- `DelegationPrefix` - Privilege-elevation command used to edit the file +- `UserKey` - Optional SSH private key for the service account + +## Limitations + +- Assumes the password is stored as a single text line that starts with `ApplicationPasswordPrefix` +- `CheckPassword` is not implemented in this sample +- The sample performs a raw text substitution and is not format-aware +- Because the full path is built by concatenation, `ApplicationFilePath` should include any required trailing path separator + +## Related + +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [Custom Parameters Reference](../../../docs/reference/custom-parameters.md) +- [Testing and Debugging Guide](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/ssh/linux-ssh-batch-mode/README.md b/samples/ssh/linux-ssh-batch-mode/README.md new file mode 100644 index 0000000..292ab73 --- /dev/null +++ b/samples/ssh/linux-ssh-batch-mode/README.md @@ -0,0 +1,53 @@ +# Linux SSH Batch Mode Example + +This sample shows how to manage Linux passwords without an interactive shell by using `ExecuteCommand`. It is a good starting point for targets where non-interactive command execution is more reliable than prompt-driven `Send` and `Receive` flows. + +## Target System + +A Linux host where the service account can run the required commands through non-interactive SSH. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Verifies the service account exists and can be resolved with `/usr/bin/id`. | +| `CheckPassword` | Reads the target account's `/etc/shadow` entry and compares the supplied password hash locally in the script. | +| `ChangePassword` | Runs `/usr/bin/passwd ` by passing the new password on stdin through `ExecuteCommand`. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for the target asset. | + +## Prerequisites + +- SPP version 7.4 or later +- A Linux host reachable over SSH +- A service account that can run `/usr/bin/cat /etc/shadow` and `/usr/bin/passwd ` via `sudo` or the configured `DelegationPrefix` +- If the delegation command requires a password, the service account password must be available so the sample can retry with `sudo -S` + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./LinuxSshBatchModeExample.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script connects with `RequestTerminal: false` and wraps every remote command in a helper that captures stdout, stderr, and exit code. `CheckSystem` and `CheckPassword` call `/usr/bin/id` and `/usr/bin/cat /etc/shadow` non-interactively, retrying with `sudo -S` when a password prompt is detected. `ChangePassword` pipes the new password twice into `passwd`, then checks stderr for the expected success message. Because the sample never relies on an interactive prompt loop, it is much shorter than the interactive SSH examples. + +## Parameters + +- `DelegationPrefix` - Optional privilege-elevation command, typically `sudo` +- `UserKey` - Optional SSH private key for the service account login + +## Limitations + +- Requires SPP 7.4 or later because it depends on `ExecuteCommand` +- Uses fixed Linux command paths such as `/usr/bin/id`, `/usr/bin/cat`, and `/usr/bin/passwd` +- Still depends on `/etc/shadow` access, so it manages local Linux accounts only +- The password-change success check is based on the expected `passwd` output text + +## Related + +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [Operations Reference](../../../docs/reference/operations.md) +- [SPP Compatibility Matrix](../../../docs/reference/compatibility.md) diff --git a/samples/ssh/restricted-authorized-key/README.md b/samples/ssh/restricted-authorized-key/README.md new file mode 100644 index 0000000..cdba8c7 --- /dev/null +++ b/samples/ssh/restricted-authorized-key/README.md @@ -0,0 +1,52 @@ +# Linux with Restricted Authorized Key Service Account + +This sample demonstrates a least-privilege SSH design where the service account authenticates only with a restricted authorized key. It uses non-interactive command execution plus passwordless `sudo` to validate and rotate local Linux account passwords. + +## Target System + +A Linux host where the Safeguard service account uses a restricted SSH key and passwordless `sudo` for a tightly limited command set. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Verifies the service account can resolve the target user through `sudo /usr/bin/id`. | +| `CheckPassword` | Reads the managed account's `/etc/shadow` entry with `sudo` and compares the supplied password to the stored hash. | +| `ChangePassword` | Runs `sudo /usr/bin/passwd ` non-interactively by sending the new password on stdin. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for the asset. | + +## Prerequisites + +- SPP version 7.4 or later +- A Linux host reachable over SSH +- A restricted service-account key configured in `UserKey` +- Passwordless `sudo` rights for the exact commands the sample runs, including `/usr/bin/id`, `/usr/bin/cat /etc/shadow`, and `/usr/bin/passwd ` + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./RestrictedAuthorizedKeyExample.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The connection is opened with `RequestTerminal: false`, and every remote command is executed through a helper that always prefixes the command with `sudo`. `CheckSystem` resolves the target account, `CheckPassword` reads the shadow entry and compares it in-script, and `ChangePassword` drives `passwd` by supplying the new password twice on stdin. Unlike the broader batch-mode sample, this example intentionally fails if `sudo` requests a password, which keeps it aligned with a restricted-key, passwordless-sudo design. + +## Parameters + +- `UserKey` - Required SSH private key for the restricted service account + +## Limitations + +- Requires SPP 7.4 or later because it depends on `ExecuteCommand` +- Assumes passwordless `sudo`; the sample throws an error if `sudo` prompts for a password +- Uses fixed Linux command paths and manages only local accounts backed by `/etc/shadow` +- The restricted key policy must still allow the exact `sudo` commands used by the sample + +## Related + +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [SSH Key Management Guide](../../../docs/guides/ssh-key-management.md) +- [SPP Compatibility Matrix](../../../docs/reference/compatibility.md) diff --git a/samples/ssh/vcenter-appliance/README.md b/samples/ssh/vcenter-appliance/README.md new file mode 100644 index 0000000..8ea8e0a --- /dev/null +++ b/samples/ssh/vcenter-appliance/README.md @@ -0,0 +1,53 @@ +# VMware vCenter Server Appliance Password Management + +This sample manages privileged accounts on VMware vCenter Server Appliance over SSH. It can validate the appliance configuration, rotate vCenter SSO administrator passwords, keep `root` synchronized when required, and discover members of the `Administrators` group. + +## Target System + +A VMware vCenter Server Appliance (VCSA) with SSH and shell access enabled. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Logs in to the appliance, confirms that `root` is a `superAdmin`, and verifies the shell can be entered successfully. | +| `CheckPassword` | Attempts SSH login using the supplied account password against the appliance's `root` login, matching the sample's root/SSO password-sync assumption. | +| `ChangePassword` | Resets the target SSO user password with `dir-cli`; when rotating the functional administrator account, it also updates `root` to keep the passwords synchronized. | +| `DiscoverAccounts` | Lists members of the VCSA `Administrators` group and emits them as discovered accounts, excluding service principals. | +| `DiscoverSshHostKey` | Retrieves the SSH host key for the asset. | + +## Prerequisites + +- SPP version 6.0 or later +- A vCenter Server Appliance reachable over SSH with appliance shell access enabled +- A Safeguard service account configured per the sample's assumption that `root` and `Administrator@vsphere.local` share the same password +- Permission to run `/usr/lib/vmware-vmafd/bin/dir-cli` and the appliance password update commands from the SSH session + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./vCenterServerAppliance.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account and managed account(s) +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The sample always connects over SSH as `root`, enters the appliance shell, and checks that `root` has the expected `superAdmin` role. For password rotation, it first resets the SSO account with `dir-cli password reset --account `, authenticating that command by sending the service-account password. If the account being rotated is the same functional administrator account used by Safeguard, the script exits the shell and runs `localaccounts.user.password.update` so the `root` password stays in sync. `DiscoverAccounts` uses `dir-cli group list --name Administrators` and filters out service principals before writing discovered accounts. + +## Parameters + +- No sample-specific custom parameters beyond the standard SSH connection and account fields + +## Limitations + +- The sample hard-codes SSH login as `root` +- It assumes `root` and `Administrator@vsphere.local` use the same password +- `CheckPassword` validates the root login path, not an arbitrary SSO account in isolation +- Discovery is limited to members of the `Administrators` group and omits service principals + +## Related + +- [Account Discovery Guide](../../../docs/guides/account-discovery.md) +- [SSH Platforms Guide](../../../docs/guides/ssh-platforms.md) +- [Testing and Debugging Guide](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/telnet/README.md b/samples/telnet/README.md new file mode 100644 index 0000000..3908bc9 --- /dev/null +++ b/samples/telnet/README.md @@ -0,0 +1,22 @@ +# Telnet Samples + +Tested custom platform scripts for managing systems over Telnet, including TN3270 for mainframe environments. + +| Sample | Complexity | Target System | +| --- | --- | --- | +| [cisco-ios](cisco-ios/) | ⭐⭐⭐ | Cisco IOS network devices | +| [racf-tn3270](racf-tn3270/) | ⭐⭐⭐ | IBM RACF mainframes via TN3270 | + +## About Telnet Platforms + +Telnet platforms use the same `Connect`, `Send`, `Receive`, and `Disconnect` commands as SSH platforms but connect over Telnet instead. The scripting patterns are nearly identical to interactive SSH — the difference is the transport layer. + +TN3270 is a specialized Telnet variant for IBM mainframe communication. The `GenericRacfTn3270` sample demonstrates screen-based interaction patterns specific to 3270 terminal emulation. + +## Related + +- [SSH Platforms Guide](../../docs/guides/ssh-platforms.md) — many patterns also apply to Telnet +- [Commands: Connect/Disconnect](../../docs/reference/commands/connect.md) — connection management +- [Commands: Send/Receive](../../docs/reference/commands/send-receive.md) — interactive terminal commands + +> **Note:** Telnet pattern files (non-custom-platform) have moved to [SafeguardAutomation](https://github.com/OneIdentity/SafeguardAutomation/tree/master/Terminal%20Pattern%20Files). diff --git a/samples/telnet/cisco-ios/README.md b/samples/telnet/cisco-ios/README.md new file mode 100644 index 0000000..2b0ae29 --- /dev/null +++ b/samples/telnet/cisco-ios/README.md @@ -0,0 +1,53 @@ +# Cisco IOS Password Management (Telnet) + +This sample manages local Cisco IOS credentials over Telnet. It can validate accounts, change local user passwords, and rotate the device's enable password while preserving the existing password style and privilege context. + +## Target System + +Cisco IOS network devices that expose Telnet and local `username` / `enable` configuration entries. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Logs in with the service account, enters enable mode, and verifies that the referenced account or enable entry exists in the running configuration. | +| `CheckPassword` | Logs in with the service account, confirms the target account exists, then performs a second Telnet login with the managed account credentials to verify the password. | +| `ChangePassword` | Logs in with the service account, detects whether the target is an `enable` password or a local `username`, updates the configuration, and saves it with `write mem`. | + +## Prerequisites + +- SPP 6.0 or later +- Telnet access from SPP to the Cisco IOS device on the configured port +- A service account with permission to enter enable mode and run `configure terminal` +- Optional `EnablePwd` if the service account does not already land in privileged exec mode + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./GenericCiscoIosTelnet.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account, managed account(s), and `EnablePwd` if the device prompts for it +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script connects over Telnet, sends the login credentials, and enters enable mode. It uses `show config | include` to find the target account definition, then branches based on whether the match is an `enable` password or a local `username` entry. For enable passwords it preserves `secret` versus `password`; for local users it preserves privilege and secret-level information and, if required by IOS, removes and recreates the username. After a successful change it exits config mode, runs `write mem`, and returns success. + +## Parameters + +- `EnablePwd`: Optional enable-mode password used by `SetupEnvironment`. +- `Timeout`: Telnet connection and receive timeout. Default: `20`. +- `Port`: Telnet port. Default: `23`. +- `AssetName`: Optional display name used in status messages; if blank, the imported `ResolveAssetName` helper can populate it. + +## Limitations + +- This sample uses Telnet only. Prefer SSH in production whenever the device supports it. +- It assumes Cisco IOS command syntax and prompt behavior, including `show config | include`, `configure terminal`, and `write mem`. +- The script writes the configuration immediately after a change. + +## Related + +- [Connect command reference](../../../docs/reference/commands/connect.md) +- [Send/Receive command reference](../../../docs/reference/commands/send-receive.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) diff --git a/samples/telnet/racf-tn3270/README.md b/samples/telnet/racf-tn3270/README.md new file mode 100644 index 0000000..05f6c02 --- /dev/null +++ b/samples/telnet/racf-tn3270/README.md @@ -0,0 +1,54 @@ +# IBM RACF Password Management (TN3270) + +This sample manages RACF passwords through a TN3270 session to a z/OS logon screen. It validates logon for both service and managed accounts and changes passwords by issuing a RACF `ALU ... PASSWORD(...) NOEXPIRED` command. + +## Target System + +IBM z/OS RACF user IDs exposed through a TN3270 / TSO logon session. + +## Operations Implemented + +| Operation | Description | +| --- | --- | +| `CheckSystem` | Connects to the TN3270 endpoint, signs in with the function account, and logs off to verify connectivity and administrative credentials. | +| `CheckPassword` | Uppercases the managed account username, signs in through the TSO/E logon prompt, and returns success only when the RACF logon completes. | +| `ChangePassword` | Verifies the target user exists, signs in with the function account, issues the `ALU` password-change command, and parses the response for success or policy failure. | + +## Prerequisites + +- SPP 6.0 or later +- Network access from SPP to the TN3270 endpoint, typically on port `23` or another site-specific port +- A RACF function account authorized for TSO logon and for the `ALU` command used to update user passwords +- Managed accounts that map to RACF user IDs; optional `WorkstationId` if your host requires a specific terminal identifier + +## Deployment + +1. Upload the script: `Import-SafeguardCustomPlatformScript -FilePath ./GenericRacfTn3270.json` +2. Create a custom platform using this script +3. Create an asset using the platform +4. Configure service account, managed account(s), and TN3270 connection options such as `UseSsl` and `WorkstationId` +5. Test with `Test-SafeguardAssetAccountPassword -ExtendedLogging` + +## How It Works + +The script uppercases RACF user IDs, connects with a TN3270 session, and sends `LOGON %UserName%`. It parses the returned screen for `INVALID USERID`, authorization failures, or the `TSO/E LOGON` prompt. For password checks it submits the managed account password and treats a normal RACF session banner such as `LAST ACCESS AT` or `LOGON IN PROGRESS` as success. For password changes it first confirms the account exists, then sends `ALU %AccountUserName% PASSWORD(%NewPassword%) NOEXPIRED`, looks for `READY` to confirm success, and logs off or disconnects as needed. + +## Parameters + +- `UseSsl`: Enables TLS for the TN3270 connection. Default: `true`. +- `SkipServerCertValidation`: Skips certificate validation when TLS is enabled. +- `WorkstationId`: Optional TN3270 workstation identifier. +- `Timeout`: Connection and receive timeout. Default: `30`. +- `Port`: TN3270 port. Default: `23`. + +## Limitations + +- The sample assumes a RACF/TSO logon flow compatible with the `TSO/E LOGON` prompt and the `ALU ... PASSWORD(...) NOEXPIRED` command syntax. +- User IDs are uppercased before use. +- If RACF rejects the new password and prompts for another one, the script disconnects instead of continuing the interactive recovery flow. + +## Related + +- [Connect command reference](../../../docs/reference/commands/connect.md) +- [Send/Receive command reference](../../../docs/reference/commands/send-receive.md) +- [Testing and debugging](../../../docs/guides/testing-and-debugging.md) From e7cc556acc4f511a353b2456b68cd860cf710145 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 21:51:33 -0600 Subject: [PATCH 05/13] Fix pre-existing JSON syntax errors in sample scripts - Remove trailing commas in LinuxSshBatchModeExample.json - Remove trailing commas in Okta_WithDiscoveryAndGroupMembershipRestore.json - Remove trailing commas in RestrictedAuthorizedKeyExample.json - Remove double comma and trailing commas in OneLogin_GRC_JIT_addon.json - Remove UTF-8 BOM from GenericRacfTn3270.json These errors pre-date the reorganization but were never caught because the CI workflow previously targeted the unused master branch. --- ...ithDiscoveryAndGroupMembershipRestore.json | 2 +- .../onelogin-jit/OneLogin_GRC_JIT_addon.json | 4 ++-- .../LinuxSshBatchModeExample.json | 20 +++++++++---------- .../RestrictedAuthorizedKeyExample.json | 18 ++++++++--------- .../telnet/racf-tn3270/GenericRacfTn3270.json | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json b/samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json index dd74ea7..63e8d97 100644 --- a/samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json +++ b/samples/http/okta-discovery/Okta_WithDiscoveryAndGroupMembershipRestore.json @@ -1171,7 +1171,7 @@ "Name": "GetGroupsOfUser", "Parameters": ["%Address%", "%{user.id}%", "%FuncPassword%", "%{SkipServerCertValidation}%", "%{UseSsl}%"], "ResultVariable": "GetGroupsOfUserResult" - }, + } }, { "ForEach": { "CollectionName": "GetGroupsOfUserResult", diff --git a/samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json b/samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json index 21642a6..4642049 100644 --- a/samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json +++ b/samples/http/onelogin-jit/OneLogin_GRC_JIT_addon.json @@ -830,7 +830,7 @@ } } }, - , + { "Function": { "Name": "DeleteUserFromRole", @@ -2163,7 +2163,7 @@ "ProxyPort": "%HttpProxyPort%", "ProxyUser": "%HttpProxyUserName%", "ProxyPassword": "%HttpProxyPassword%" - }, + } }, { diff --git a/samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json b/samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json index f88d299..db7f867 100644 --- a/samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json +++ b/samples/ssh/linux-ssh-batch-mode/LinuxSshBatchModeExample.json @@ -71,7 +71,7 @@ "Then": { "Do": [ { "Return" : { "Value": true }} ] } } }, - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] } } }, { "Return" : { "Value" : false } } ] }, @@ -136,7 +136,7 @@ } }, { "Log": { "Text": "Failed to change password : output:%{ Result.Stdout }% , err: %{ Result.Stderr }%" } }, - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] } } }, { "Return": { "Value": false } } @@ -169,7 +169,7 @@ } }, { "Log": { "Text": "Successfully connected to the asset using key authentication" } }, - { "Status": { "Type": "Checking", "Percent": 40, "Message": { "Name": "SystemLoginCheck", "Parameters": [ "%Address%" ] } } }, + { "Status": { "Type": "Checking", "Percent": 40, "Message": { "Name": "SystemLoginCheck", "Parameters": [ "%Address%" ] } } } ], "Catch": [ { "Status": { "Type": "Connecting", "Percent": 95, "Message": { "Name": "AssetConnectFailedWithReasonAndAddress", "Parameters": [ "%AssetName%", "%Address%", "%Exception%" ] } } }, @@ -187,7 +187,7 @@ { "StdinArgs": { "Type": "Array" } }, { "CommandContainsSecret": { "Type": "Boolean" } }, { "InputContainsSecret": { "Type": "Boolean" } }, - { "OutputContainsSecret": { "Type": "Boolean" } }, + { "OutputContainsSecret": { "Type": "Boolean" } } ], "Do": [ { "SetItem" : { "Name": "rc", "Value": 1 }}, @@ -199,7 +199,7 @@ { "SetItem": { "Name": "runcmd", "Value": "%{ Cmd }%" }}, { "Condition": { "If" : "UseSudo && !string.IsNullOrEmpty(DelegationPrefix)", - "Then": { "Do": [ { "SetItem": { "Name": "runcmd", "Value": "%DelegationPrefix% %{ Cmd }%" }}, ]}} + "Then": { "Do": [ { "SetItem": { "Name": "runcmd", "Value": "%DelegationPrefix% %{ Cmd }%" }} ]}} }, { "ExecuteCommand": { @@ -260,7 +260,7 @@ "If": "rc == 0", "Then": { "Do": [ - { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%", } } } + { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%" } } } ] } } @@ -276,7 +276,7 @@ } } }, - { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%", } } } + { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%" } } } ] }, { @@ -316,20 +316,20 @@ "If": "Result.rc != 0", "Then": { "Do": [ - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "rc : ${ rc }% Error: %{ Result.Stderr }%" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "rc : ${ rc }% Error: %{ Result.Stderr }%" ] } } }, { "Throw" : { "Value" : "Failed to check password" } } ] } } }, - { "SetItem": { "Name": "MatchLine", "Value": "%{ Regex.Match(Result.Stdout, $\"^{AccountUserName}:([^:]+)\", RegexOptions.MultiLine) }%", } }, + { "SetItem": { "Name": "MatchLine", "Value": "%{ Regex.Match(Result.Stdout, $\"^{AccountUserName}:([^:]+)\", RegexOptions.MultiLine) }%" } }, { "Condition": { "If": "!MatchLine.Success", "Then": { "Do": [ { "Log" : { "Text": "Failed to retrieve password hash for account" }}, - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Failed to retrieve password hash for account" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Failed to retrieve password hash for account" ] } } }, { "Throw" : { "Value" : "Failed to check password" } } ] } diff --git a/samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json b/samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json index 3c88453..3efcaa5 100644 --- a/samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json +++ b/samples/ssh/restricted-authorized-key/RestrictedAuthorizedKeyExample.json @@ -69,7 +69,7 @@ "Then": { "Do": [ { "Return" : { "Value": true }} ] } } }, - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] } } }, { "Return" : { "Value" : false } } ] }, @@ -130,7 +130,7 @@ } }, { "Log": { "Text": "Failed to change password : output:%{ Result.Stdout }% , err: %{ Result.Stderr }%" } }, - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Output: %{ Result.Stdout }% Error: %{ Result.Stderr }%" ] } } }, { "Return": { "Value": false } } @@ -162,7 +162,7 @@ } }, { "Log": { "Text": "Successfully connected to the asset using key authentication" } }, - { "Status": { "Type": "Checking", "Percent": 40, "Message": { "Name": "SystemLoginCheck", "Parameters": [ "%Address%" ] } } }, + { "Status": { "Type": "Checking", "Percent": 40, "Message": { "Name": "SystemLoginCheck", "Parameters": [ "%Address%" ] } } } ], "Catch": [ { "Status": { "Type": "Connecting", "Percent": 95, "Message": { "Name": "AssetConnectFailedWithReasonAndAddress", "Parameters": [ "%AssetName%", "%Address%", "%Exception%" ] } } }, @@ -179,7 +179,7 @@ { "StdinArgs": { "Type": "Array" } }, { "CommandContainsSecret": { "Type": "Boolean" } }, { "InputContainsSecret": { "Type": "Boolean" } }, - { "OutputContainsSecret": { "Type": "Boolean" } }, + { "OutputContainsSecret": { "Type": "Boolean" } } ], "Do": [ { "SetItem" : { "Name": "rc", "Value": 1 }}, @@ -226,7 +226,7 @@ "If": "rc == 0", "Then": { "Do": [ - { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%", } } } + { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%" } } } ] } } @@ -242,7 +242,7 @@ } } }, - { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%", } } } + { "Return": { "Value": { "rc": "%{ rc }%", "Stdout": "%{ Stdout }%", "Stderr": "%{ Stderr }%" } } } ] }, { @@ -282,20 +282,20 @@ "If": "Result.rc != 0", "Then": { "Do": [ - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "rc : ${ rc }% Error: %{ Result.Stderr }%" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "rc : ${ rc }% Error: %{ Result.Stderr }%" ] } } }, { "Throw" : { "Value" : "Failed to check password" } } ] } } }, - { "SetItem": { "Name": "MatchLine", "Value": "%{ Regex.Match(Result.Stdout, $\"^{AccountUserName}:([^:]+)\", RegexOptions.MultiLine) }%", } }, + { "SetItem": { "Name": "MatchLine", "Value": "%{ Regex.Match(Result.Stdout, $\"^{AccountUserName}:([^:]+)\", RegexOptions.MultiLine) }%" } }, { "Condition": { "If": "!MatchLine.Success", "Then": { "Do": [ { "Log" : { "Text": "Failed to retrieve password hash for account" }}, - { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Failed to retrieve password hash for account" ] }, } }, + { "Status": { "Type": "Checking", "Percent": "90", "Message": { "Name": "UnexpectedDataReceived", "Parameters": [ "Failed to retrieve password hash for account" ] } } }, { "Throw" : { "Value" : "Failed to check password" } } ] } diff --git a/samples/telnet/racf-tn3270/GenericRacfTn3270.json b/samples/telnet/racf-tn3270/GenericRacfTn3270.json index 4e47af0..61d1387 100644 --- a/samples/telnet/racf-tn3270/GenericRacfTn3270.json +++ b/samples/telnet/racf-tn3270/GenericRacfTn3270.json @@ -1,4 +1,4 @@ -{ +{ "Id": "GenericRacfTn3270", "BackEnd": "Scriptable", From 3de07a33c556c43ba6867ceec04ee2a2f4c8d40f Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 21:57:28 -0600 Subject: [PATCH 06/13] Add HTTP form-fill quick-start guide Covers the browser-form login pattern: fetch page, extract form, fill credentials, submit. Includes guidance on adapting field names, XPath selection, cookie handling, and ContainsSecret masking. --- docs/quick-start/README.md | 1 + docs/quick-start/http-form-fill.md | 134 +++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 docs/quick-start/http-form-fill.md diff --git a/docs/quick-start/README.md b/docs/quick-start/README.md index 38adf28..79980f5 100644 --- a/docs/quick-start/README.md +++ b/docs/quick-start/README.md @@ -6,5 +6,6 @@ Pick the path that matches your target system and get a working custom platform | --- | --- | | A Linux/Unix system over SSH | [SSH Password Change](ssh-password-change.md) | | A REST API over HTTP | [HTTP API Check](http-api-check.md) | +| A web application with a login form | [HTTP Form-Fill](http-form-fill.md) | These guides skip the theory and focus on getting a result. For deeper understanding, see [Concepts](../concepts/) or [Tutorials](../tutorials/). diff --git a/docs/quick-start/http-form-fill.md b/docs/quick-start/http-form-fill.md new file mode 100644 index 0000000..22e6501 --- /dev/null +++ b/docs/quick-start/http-form-fill.md @@ -0,0 +1,134 @@ +# Quick Start: HTTP Form-Fill Password Check + +Get a working custom platform that validates credentials against a web login form in under 5 minutes. + +## What You'll Get + +A minimal platform script that navigates to a login page, fills in the username and password fields, submits the form, and checks whether the login succeeded — the same pattern used by the Facebook and Twitter samples. + +## When to Use Form-Fill vs. REST API + +| Approach | Use when... | +| --- | --- | +| **REST API** ([HTTP API Quick Start](http-api-check.md)) | The target exposes a programmatic API with JSON responses | +| **Form-Fill** (this guide) | The target only has a web login page with HTML forms | + +## Steps + +### 1. Start with the Minimal HTTP Template + +Download or copy [`TemplateHttpMinimal.json`](../../templates/TemplateHttpMinimal.json) and rename it (e.g., `MyWebAppFormFill.json`). + +### 2. Replace CheckSystem with a Form-Fill Login + +Replace the `CheckSystem` operation with one that fetches the login page, extracts the form, fills in credentials, and submits: + +```json +"CheckSystem": { + "Do": [ + { "BaseAddress": { "Address": "https://%Address%" } }, + { "Cookies": { "Enabled": true } }, + { "NewHttpRequest": { "ObjectName": "LoginPageReq" } }, + { "Request": { + "Verb": "Get", + "Url": "/login", + "RequestObjectName": "LoginPageReq", + "ResponseObjectName": "LoginPageResp" + } }, + { "ExtractFormData": { + "ResponseObjectName": "LoginPageResp", + "FormObjectName": "LoginForm" + } }, + { + "Condition": { + "If": "LoginForm == null", + "Then": { "Do": [ + { "Throw": { "Message": "Login form not found on page" } } + ] } + } + }, + { "SetFormValue": { + "FormObjectName": "LoginForm", + "InputName": "username", + "Value": "%FuncUserName%" + } }, + { "SetFormValue": { + "FormObjectName": "LoginForm", + "InputName": "password", + "Value": "%FuncPassword%", + "ContainsSecret": true + } }, + { "NewHttpRequest": { "ObjectName": "LoginPostReq" } }, + { "Request": { + "Verb": "Post", + "Url": "%LoginForm.Action%", + "Body": "%LoginForm%", + "ContentType": "application/x-www-form-urlencoded", + "RequestObjectName": "LoginPostReq", + "ResponseObjectName": "LoginPostResp" + } }, + { + "Condition": { + "If": "Response.StatusCode == 200 || Response.StatusCode == 302", + "Then": { "Do": [{ "Return": { "Value": true } }] } + } + }, + { "Throw": { "Message": "Login failed: HTTP %Response.StatusCode%" } } + ] +} +``` + +### 3. Adapt for Your Login Page + +You'll likely need to adjust: + +| What to change | How to find it | +| --- | --- | +| Login URL (`/login`) | Open the page in a browser and note the URL | +| Form field names (`username`, `password`) | Inspect the `` elements in the HTML source | +| Success condition | Some sites redirect (302), others return 200 with a session cookie | + +> **Tip:** If the page has multiple `
` elements, add an `"XPath"` parameter to `ExtractFormData` to select the right one: +> ```json +> "XPath": "//form[@id='login-form']" +> ``` + +### 4. Enable Cookies + +Form-fill workflows almost always require cookies for session tracking. The `{ "Cookies": { "Enabled": true } }` step at the top handles this. Cookies will persist across requests within the same operation. + +### 5. Upload to SPP + +```powershell +Import-SafeguardCustomPlatformScript -FilePath .\MyWebAppFormFill.json +``` + +### 6. Create an Asset + +1. In the SPP web UI, go to **Asset Management > Assets > Add** +2. Set the platform to your new custom platform +3. Set the network address to the web application hostname +4. Assign a service account with valid login credentials + +### 7. Test + +```powershell +Test-SafeguardAssetConnection -AssetToUse "MyWebApp" -ExtendedLogging +``` + +If it reports success, SPP can log in to your web application. + +## Key Concepts for Form-Fill + +- **`ExtractFormData`** parses HTML and extracts all `` fields, including hidden CSRF tokens +- **`SetFormValue`** updates a field before submission — hidden fields are preserved automatically +- **`Cookies`** must be enabled so session cookies flow between requests +- **`ContainsSecret: true`** on password fields prevents credentials from appearing in logs +- The form's `Action` attribute is available as `%LoginForm.Action%` for the submit URL + +## Next Steps + +- Add `CheckPassword` and `ChangePassword` — study the [Facebook sample](../../samples/http/facebook/) for the full pattern +- Learn about CSRF tokens and multi-step forms — see [Forms Reference](../reference/commands/forms.md) +- Handle login challenges (CAPTCHAs, MFA prompts) — see the [Twitter sample](../../samples/http/twitter/) +- Read about HTTP cookie management — see [Cookies Reference](../reference/commands/cookies.md) From 324f1d88229f16b30865f5ffcb8cf4dbe92bd9ec Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:02:01 -0600 Subject: [PATCH 07/13] Replace internal project name with 'scripting engine' --- docs/concepts/script-execution-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/script-execution-model.md b/docs/concepts/script-execution-model.md index dc3bd72..5af50a0 100644 --- a/docs/concepts/script-execution-model.md +++ b/docs/concepts/script-execution-model.md @@ -38,7 +38,7 @@ Custom parameters retain their configured default values unless overridden at th ### 3. Script Engine Execution -The Hercules script engine processes the operation's `Do` array sequentially: +The scripting engine processes the operation's `Do` array sequentially: 1. Each command in the `Do` array is executed in order. 2. Variable interpolation (`%VariableName%`) is resolved at execution time. From 24d9c4bf005f67c7c67f7f3a276342a2d2284a37 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:04:02 -0600 Subject: [PATCH 08/13] Consolidate concepts README into single ordered list --- docs/concepts/README.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/docs/concepts/README.md b/docs/concepts/README.md index fc80b60..6406ba0 100644 --- a/docs/concepts/README.md +++ b/docs/concepts/README.md @@ -2,16 +2,9 @@ Understand how Safeguard custom platforms work before you start building. -| Document | Description | -| --- | --- | -| [Architecture](architecture.md) | What custom platforms are, the execution model, and when you need one. | -| [Script Execution Model](script-execution-model.md) | How SPP loads, validates, expands, and runs your script at task time. | -| [Feature Flags](feature-flags.md) | How SPP automatically derives platform capabilities from your script content. | -| [Platform Lifecycle](platform-lifecycle.md) | The full lifecycle: authoring → upload → testing → production → updates. | +Read in this order: -## Suggested Reading Order - -1. Start with [Architecture](architecture.md) if you're completely new. -2. Read [Feature Flags](feature-flags.md) to understand why your script *is* your configuration. -3. Read [Script Execution Model](script-execution-model.md) when you want to understand what happens at runtime. -4. Read [Platform Lifecycle](platform-lifecycle.md) when you're ready to deploy to production. +1. [Architecture](architecture.md) — What custom platforms are, the execution model, and when you need one. +2. [Feature Flags](feature-flags.md) — How SPP automatically derives platform capabilities from your script content. +3. [Script Execution Model](script-execution-model.md) — How SPP loads, validates, expands, and runs your script at task time. +4. [Platform Lifecycle](platform-lifecycle.md) — The full lifecycle: authoring → upload → testing → production → updates. From 4aa3be4ab07c714d9c12d11e894f8e3dd9a5174b Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:05:43 -0600 Subject: [PATCH 09/13] Add parent backlinks to all pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every page now has a [← Parent](link) backlink on the first line, pointing to its immediate section README. Updated existing backlinks that pointed directly to docs/README.md to point to their section index instead (e.g., Guides, Concepts, Tutorials, Reference). --- docs/README.md | 2 ++ docs/concepts/README.md | 2 ++ docs/concepts/architecture.md | 2 +- docs/concepts/feature-flags.md | 2 +- docs/concepts/platform-lifecycle.md | 2 ++ docs/concepts/script-execution-model.md | 2 ++ docs/guides/README.md | 2 ++ docs/guides/account-discovery.md | 2 +- docs/guides/api-key-management.md | 2 +- docs/guides/dependent-systems.md | 2 +- docs/guides/development-workflow.md | 2 +- docs/guides/error-handling.md | 2 +- docs/guides/file-management.md | 2 +- docs/guides/http-platforms.md | 2 +- docs/guides/jit-elevation.md | 2 +- docs/guides/regex-patterns.md | 2 +- docs/guides/ssh-key-management.md | 2 +- docs/guides/ssh-platforms.md | 2 +- docs/guides/testing-and-debugging.md | 2 +- docs/guides/troubleshooting.md | 2 +- docs/quick-start/README.md | 2 ++ docs/quick-start/http-api-check.md | 2 ++ docs/quick-start/http-form-fill.md | 2 ++ docs/quick-start/ssh-password-change.md | 2 ++ docs/reference/README.md | 2 ++ docs/reference/commands/index.md | 2 +- docs/reference/custom-parameters.md | 2 +- docs/reference/imports.md | 2 +- docs/reference/operations.md | 2 +- docs/reference/reserved-parameters.md | 2 +- docs/reference/script-structure.md | 2 +- docs/reference/status-messages.md | 2 +- docs/reference/variables.md | 2 +- docs/tutorials/README.md | 2 ++ docs/tutorials/building-a-complete-platform.md | 2 ++ docs/tutorials/your-first-form-script.md | 2 +- docs/tutorials/your-first-http-script.md | 2 +- docs/tutorials/your-first-ssh-script.md | 2 +- samples/README.md | 2 ++ samples/http/README.md | 2 ++ samples/http/facebook/README.md | 2 ++ samples/http/forgerock-openam/README.md | 2 ++ samples/http/okta-discovery/README.md | 2 ++ samples/http/onelogin-jit/README.md | 2 ++ samples/http/twitter/README.md | 2 ++ samples/http/wordpress/README.md | 2 ++ samples/ssh/README.md | 2 ++ samples/ssh/generic-linux-ssh-keys/README.md | 2 ++ samples/ssh/generic-linux-with-ad/README.md | 2 ++ samples/ssh/generic-linux-with-discovery/README.md | 2 ++ samples/ssh/generic-linux/README.md | 2 ++ samples/ssh/linux-app-text-config/README.md | 2 ++ samples/ssh/linux-ssh-batch-mode/README.md | 2 ++ samples/ssh/restricted-authorized-key/README.md | 2 ++ samples/ssh/vcenter-appliance/README.md | 2 ++ samples/telnet/README.md | 2 ++ samples/telnet/cisco-ios/README.md | 2 ++ samples/telnet/racf-tn3270/README.md | 2 ++ 58 files changed, 90 insertions(+), 26 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6d698ab..52d43f9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,5 @@ +[← Home](../README.md) + # Documentation Everything you need to learn, build, and maintain Safeguard custom platform scripts. diff --git a/docs/concepts/README.md b/docs/concepts/README.md index 6406ba0..bb3fa86 100644 --- a/docs/concepts/README.md +++ b/docs/concepts/README.md @@ -1,3 +1,5 @@ +[← Documentation](../README.md) + # Concepts Understand how Safeguard custom platforms work before you start building. diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index 7e62d4a..e298237 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Concepts](README.md) # Architecture Overview diff --git a/docs/concepts/feature-flags.md b/docs/concepts/feature-flags.md index 738f16d..6e4c79a 100644 --- a/docs/concepts/feature-flags.md +++ b/docs/concepts/feature-flags.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Concepts](README.md) # Platform Feature Flags diff --git a/docs/concepts/platform-lifecycle.md b/docs/concepts/platform-lifecycle.md index e56d028..1ac811c 100644 --- a/docs/concepts/platform-lifecycle.md +++ b/docs/concepts/platform-lifecycle.md @@ -1,3 +1,5 @@ +[← Concepts](README.md) + # Platform Lifecycle This document covers the full lifecycle of a custom platform — from initial development through production deployment and ongoing maintenance. diff --git a/docs/concepts/script-execution-model.md b/docs/concepts/script-execution-model.md index 5af50a0..79cc4c6 100644 --- a/docs/concepts/script-execution-model.md +++ b/docs/concepts/script-execution-model.md @@ -1,3 +1,5 @@ +[← Concepts](README.md) + # Script Execution Model This document explains what happens when SPP runs a custom platform script — from the moment you upload a JSON file to the moment a task executes against a target system. diff --git a/docs/guides/README.md b/docs/guides/README.md index 713fec1..7004598 100644 --- a/docs/guides/README.md +++ b/docs/guides/README.md @@ -1,3 +1,5 @@ +[← Documentation](../README.md) + # Guides Task-focused how-to documentation for building and maintaining custom platforms. Each guide covers a specific topic in depth. diff --git a/docs/guides/account-discovery.md b/docs/guides/account-discovery.md index f38234f..59c81cd 100644 --- a/docs/guides/account-discovery.md +++ b/docs/guides/account-discovery.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Account Discovery Guide diff --git a/docs/guides/api-key-management.md b/docs/guides/api-key-management.md index 75dc543..568c2b9 100644 --- a/docs/guides/api-key-management.md +++ b/docs/guides/api-key-management.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # API Key Management diff --git a/docs/guides/dependent-systems.md b/docs/guides/dependent-systems.md index 17a4316..00088ea 100644 --- a/docs/guides/dependent-systems.md +++ b/docs/guides/dependent-systems.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Dependent Systems Guide diff --git a/docs/guides/development-workflow.md b/docs/guides/development-workflow.md index ef5d108..7950a50 100644 --- a/docs/guides/development-workflow.md +++ b/docs/guides/development-workflow.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Development Workflow diff --git a/docs/guides/error-handling.md b/docs/guides/error-handling.md index 53b6bd4..7f46aaf 100644 --- a/docs/guides/error-handling.md +++ b/docs/guides/error-handling.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Error Handling Guide diff --git a/docs/guides/file-management.md b/docs/guides/file-management.md index 4caa7dd..eb7b4aa 100644 --- a/docs/guides/file-management.md +++ b/docs/guides/file-management.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # File Management Guide diff --git a/docs/guides/http-platforms.md b/docs/guides/http-platforms.md index 2cddbf5..d4f7d1f 100644 --- a/docs/guides/http-platforms.md +++ b/docs/guides/http-platforms.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # HTTP/REST API Platforms diff --git a/docs/guides/jit-elevation.md b/docs/guides/jit-elevation.md index f27f2d0..d543ec3 100644 --- a/docs/guides/jit-elevation.md +++ b/docs/guides/jit-elevation.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # JIT Elevation and Account Lifecycle Operations diff --git a/docs/guides/regex-patterns.md b/docs/guides/regex-patterns.md index b7f0d94..237a289 100644 --- a/docs/guides/regex-patterns.md +++ b/docs/guides/regex-patterns.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Regex Patterns Guide diff --git a/docs/guides/ssh-key-management.md b/docs/guides/ssh-key-management.md index 6fda995..153fb78 100644 --- a/docs/guides/ssh-key-management.md +++ b/docs/guides/ssh-key-management.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # SSH Key Management Guide diff --git a/docs/guides/ssh-platforms.md b/docs/guides/ssh-platforms.md index b038663..9b60087 100644 --- a/docs/guides/ssh-platforms.md +++ b/docs/guides/ssh-platforms.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # SSH Platforms Guide diff --git a/docs/guides/testing-and-debugging.md b/docs/guides/testing-and-debugging.md index 6f9f53f..20c7a42 100644 --- a/docs/guides/testing-and-debugging.md +++ b/docs/guides/testing-and-debugging.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Testing and Debugging diff --git a/docs/guides/troubleshooting.md b/docs/guides/troubleshooting.md index e972c3d..0795247 100644 --- a/docs/guides/troubleshooting.md +++ b/docs/guides/troubleshooting.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Guides](README.md) # Troubleshooting diff --git a/docs/quick-start/README.md b/docs/quick-start/README.md index 79980f5..d80fe7d 100644 --- a/docs/quick-start/README.md +++ b/docs/quick-start/README.md @@ -1,3 +1,5 @@ +[← Documentation](../README.md) + # Quick Start Pick the path that matches your target system and get a working custom platform in under 5 minutes. diff --git a/docs/quick-start/http-api-check.md b/docs/quick-start/http-api-check.md index 19d1b77..1e6bbad 100644 --- a/docs/quick-start/http-api-check.md +++ b/docs/quick-start/http-api-check.md @@ -1,3 +1,5 @@ +[← Quick Start](README.md) + # Quick Start: HTTP API Check Get a working custom platform that validates connectivity to a REST API over HTTP in under 5 minutes. diff --git a/docs/quick-start/http-form-fill.md b/docs/quick-start/http-form-fill.md index 22e6501..a243ebc 100644 --- a/docs/quick-start/http-form-fill.md +++ b/docs/quick-start/http-form-fill.md @@ -1,3 +1,5 @@ +[← Quick Start](README.md) + # Quick Start: HTTP Form-Fill Password Check Get a working custom platform that validates credentials against a web login form in under 5 minutes. diff --git a/docs/quick-start/ssh-password-change.md b/docs/quick-start/ssh-password-change.md index a707153..677ee83 100644 --- a/docs/quick-start/ssh-password-change.md +++ b/docs/quick-start/ssh-password-change.md @@ -1,3 +1,5 @@ +[← Quick Start](README.md) + # Quick Start: SSH Password Check Get a working custom platform that validates a Linux password over SSH in under 5 minutes. diff --git a/docs/reference/README.md b/docs/reference/README.md index 3b0d018..7f65f60 100644 --- a/docs/reference/README.md +++ b/docs/reference/README.md @@ -1,3 +1,5 @@ +[← Documentation](../README.md) + # Reference Detailed reference documentation for every aspect of custom platform scripts. Use this section when you need to look up a specific command, parameter, operation, or behavior. diff --git a/docs/reference/commands/index.md b/docs/reference/commands/index.md index 4b20e2f..7bfdbdb 100644 --- a/docs/reference/commands/index.md +++ b/docs/reference/commands/index.md @@ -1,4 +1,4 @@ -[← Documentation](../../README.md) +[← Reference](../README.md) # Commands Reference diff --git a/docs/reference/custom-parameters.md b/docs/reference/custom-parameters.md index 21a5fab..ca673fb 100644 --- a/docs/reference/custom-parameters.md +++ b/docs/reference/custom-parameters.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # Custom Parameters Reference diff --git a/docs/reference/imports.md b/docs/reference/imports.md index 51e6bd1..9f2aa3d 100644 --- a/docs/reference/imports.md +++ b/docs/reference/imports.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # System Import Libraries Reference diff --git a/docs/reference/operations.md b/docs/reference/operations.md index cec76a2..5168050 100644 --- a/docs/reference/operations.md +++ b/docs/reference/operations.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # Operations Reference diff --git a/docs/reference/reserved-parameters.md b/docs/reference/reserved-parameters.md index ac4415e..7b8ba6f 100644 --- a/docs/reference/reserved-parameters.md +++ b/docs/reference/reserved-parameters.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # Reserved Parameters Reference diff --git a/docs/reference/script-structure.md b/docs/reference/script-structure.md index 0a9e6fc..5b285d9 100644 --- a/docs/reference/script-structure.md +++ b/docs/reference/script-structure.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # Script Structure Reference diff --git a/docs/reference/status-messages.md b/docs/reference/status-messages.md index eedabf7..487df5c 100644 --- a/docs/reference/status-messages.md +++ b/docs/reference/status-messages.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # Status Messages Reference diff --git a/docs/reference/variables.md b/docs/reference/variables.md index 3a2e589..919eec9 100644 --- a/docs/reference/variables.md +++ b/docs/reference/variables.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Reference](README.md) # Variables Reference diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 0a89360..32267c1 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -1,3 +1,5 @@ +[← Documentation](../README.md) + # Tutorials Step-by-step walkthroughs that teach you how to build custom platform scripts from scratch. diff --git a/docs/tutorials/building-a-complete-platform.md b/docs/tutorials/building-a-complete-platform.md index a132bb5..b0a2034 100644 --- a/docs/tutorials/building-a-complete-platform.md +++ b/docs/tutorials/building-a-complete-platform.md @@ -1,3 +1,5 @@ +[← Tutorials](README.md) + # Building a Complete Platform This tutorial takes you from a minimal custom platform script to a full-featured one with multiple operations. By the end, you'll have a platform that can check connectivity, validate passwords, change passwords, and discover accounts — all over SSH. diff --git a/docs/tutorials/your-first-form-script.md b/docs/tutorials/your-first-form-script.md index 8286dbb..0cbfa62 100644 --- a/docs/tutorials/your-first-form-script.md +++ b/docs/tutorials/your-first-form-script.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Tutorials](README.md) # Your First Form-Based Script diff --git a/docs/tutorials/your-first-http-script.md b/docs/tutorials/your-first-http-script.md index 0646f80..dd12f28 100644 --- a/docs/tutorials/your-first-http-script.md +++ b/docs/tutorials/your-first-http-script.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Tutorials](README.md) # Your First HTTP Script diff --git a/docs/tutorials/your-first-ssh-script.md b/docs/tutorials/your-first-ssh-script.md index 148bb8c..74c7611 100644 --- a/docs/tutorials/your-first-ssh-script.md +++ b/docs/tutorials/your-first-ssh-script.md @@ -1,4 +1,4 @@ -[← Documentation](../README.md) +[← Tutorials](README.md) # Your First SSH Script diff --git a/samples/README.md b/samples/README.md index cc6d4a4..0ff50cb 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,3 +1,5 @@ +[← Home](../README.md) + # Sample Scripts Production-tested custom platform scripts you can deploy to SPP or study as reference implementations. Each sample includes a companion README explaining what it does, how to set it up, and how to deploy it. diff --git a/samples/http/README.md b/samples/http/README.md index c0aa29b..de366b4 100644 --- a/samples/http/README.md +++ b/samples/http/README.md @@ -1,3 +1,5 @@ +[← Samples](../README.md) + # HTTP Samples Tested custom platform scripts for managing systems over HTTP/REST APIs. These samples cover REST APIs with various authentication methods, browser-form login workflows, and cloud service integrations. diff --git a/samples/http/facebook/README.md b/samples/http/facebook/README.md index b362bc0..4fef2d6 100644 --- a/samples/http/facebook/README.md +++ b/samples/http/facebook/README.md @@ -1,3 +1,5 @@ +[← HTTP Samples](../README.md) + # Facebook Password Management (HTTP Form) This sample validates and changes Facebook account passwords by replaying Facebook's browser-based login and security forms. It is a form-scraping example for sites that do not expose a supported administrative API. diff --git a/samples/http/forgerock-openam/README.md b/samples/http/forgerock-openam/README.md index 82717b8..e9bb577 100644 --- a/samples/http/forgerock-openam/README.md +++ b/samples/http/forgerock-openam/README.md @@ -1,3 +1,5 @@ +[← HTTP Samples](../README.md) + # ForgeRock AM Password Management (HTTP API) This sample manages ForgeRock AM/OpenAM users through the REST API for a specific realm. It uses header-based authentication to validate credentials and to set a user's `userpassword` attribute. diff --git a/samples/http/okta-discovery/README.md b/samples/http/okta-discovery/README.md index 92d4d16..7556c9d 100644 --- a/samples/http/okta-discovery/README.md +++ b/samples/http/okta-discovery/README.md @@ -1,3 +1,5 @@ +[← HTTP Samples](../README.md) + # Okta Discovery and Group Restore (HTTP API) This sample combines password validation, password reset, account discovery, and group-membership restore/suspend workflows for Okta. It uses an Okta API token for administrative operations and the `/authn` endpoint for end-user password checks. diff --git a/samples/http/onelogin-jit/README.md b/samples/http/onelogin-jit/README.md index b4d5063..c25c351 100644 --- a/samples/http/onelogin-jit/README.md +++ b/samples/http/onelogin-jit/README.md @@ -1,3 +1,5 @@ +[← HTTP Samples](../README.md) + # OneLogin JIT Account Lifecycle and Role Elevation This sample is a JIT-focused add-on for OneLogin environments already managed through a separate Generic REST connector. It validates OAuth client credentials, enables or disables users, and elevates or demotes users by assigning or removing OneLogin roles. diff --git a/samples/http/twitter/README.md b/samples/http/twitter/README.md index c4737a6..a6a12ef 100644 --- a/samples/http/twitter/README.md +++ b/samples/http/twitter/README.md @@ -1,3 +1,5 @@ +[← HTTP Samples](../README.md) + # Twitter Password Management (HTTP Form) This sample validates and changes Twitter account passwords by walking the site's login and settings forms over HTTP. It also detects several common challenge states that prevent unattended password management. diff --git a/samples/http/wordpress/README.md b/samples/http/wordpress/README.md index 65c1586..cc5c16f 100644 --- a/samples/http/wordpress/README.md +++ b/samples/http/wordpress/README.md @@ -1,3 +1,5 @@ +[← HTTP Samples](../README.md) + # WordPress Password Management (Basic Auth API) This sample manages WordPress users through the REST API using HTTP Basic authentication. It is a straightforward API example for environments where the site exposes authenticated REST endpoints for user administration. diff --git a/samples/ssh/README.md b/samples/ssh/README.md index af9f150..5a07c5a 100644 --- a/samples/ssh/README.md +++ b/samples/ssh/README.md @@ -1,3 +1,5 @@ +[← Samples](../README.md) + # SSH Samples Tested custom platform scripts for managing systems over SSH. These samples cover interactive expect-style patterns and batch-mode execution. diff --git a/samples/ssh/generic-linux-ssh-keys/README.md b/samples/ssh/generic-linux-ssh-keys/README.md index ab04a6c..9783534 100644 --- a/samples/ssh/generic-linux-ssh-keys/README.md +++ b/samples/ssh/generic-linux-ssh-keys/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Generic Linux with SSH Key Support This sample adds SSH authorized-key management to the generic Linux password-management flow. It can discover existing keys, check whether a specific key is installed, add a new key, optionally test the matching private key, and remove an old key. diff --git a/samples/ssh/generic-linux-with-ad/README.md b/samples/ssh/generic-linux-with-ad/README.md index dd8a0ac..3087b3c 100644 --- a/samples/ssh/generic-linux-with-ad/README.md +++ b/samples/ssh/generic-linux-with-ad/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Generic Linux with Active Directory Service Account This sample is the generic Linux SSH password-management script with an optional Active Directory-style login name for the service account. It appends `@domain` during SSH login, then uses the same local `/etc/passwd` and `/etc/shadow` workflow to validate and change managed-account passwords. diff --git a/samples/ssh/generic-linux-with-discovery/README.md b/samples/ssh/generic-linux-with-discovery/README.md index f9d0494..93c442c 100644 --- a/samples/ssh/generic-linux-with-discovery/README.md +++ b/samples/ssh/generic-linux-with-discovery/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Generic Linux with Account Discovery This sample extends the generic Linux SSH password-management flow with account discovery. In addition to checking and changing passwords, it enumerates Unix accounts and group memberships and reports them back to Safeguard. diff --git a/samples/ssh/generic-linux/README.md b/samples/ssh/generic-linux/README.md index b57cfb0..dde87ec 100644 --- a/samples/ssh/generic-linux/README.md +++ b/samples/ssh/generic-linux/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Generic Linux Password Management This sample manages local Linux account passwords over SSH using an interactive shell. It verifies service-account access, validates managed-account passwords against `/etc/shadow`, and changes passwords with `passwd`. diff --git a/samples/ssh/linux-app-text-config/README.md b/samples/ssh/linux-app-text-config/README.md index 4b72e5a..263f189 100644 --- a/samples/ssh/linux-app-text-config/README.md +++ b/samples/ssh/linux-app-text-config/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Linux Application Password in a Text Configuration File This sample changes an application password stored in a plain-text configuration file on Linux over SSH. It is intended for simple legacy applications where password rotation means replacing a single `prefix + password` line in a file. diff --git a/samples/ssh/linux-ssh-batch-mode/README.md b/samples/ssh/linux-ssh-batch-mode/README.md index 292ab73..c25051b 100644 --- a/samples/ssh/linux-ssh-batch-mode/README.md +++ b/samples/ssh/linux-ssh-batch-mode/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Linux SSH Batch Mode Example This sample shows how to manage Linux passwords without an interactive shell by using `ExecuteCommand`. It is a good starting point for targets where non-interactive command execution is more reliable than prompt-driven `Send` and `Receive` flows. diff --git a/samples/ssh/restricted-authorized-key/README.md b/samples/ssh/restricted-authorized-key/README.md index cdba8c7..cd65901 100644 --- a/samples/ssh/restricted-authorized-key/README.md +++ b/samples/ssh/restricted-authorized-key/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # Linux with Restricted Authorized Key Service Account This sample demonstrates a least-privilege SSH design where the service account authenticates only with a restricted authorized key. It uses non-interactive command execution plus passwordless `sudo` to validate and rotate local Linux account passwords. diff --git a/samples/ssh/vcenter-appliance/README.md b/samples/ssh/vcenter-appliance/README.md index 8ea8e0a..f50b80f 100644 --- a/samples/ssh/vcenter-appliance/README.md +++ b/samples/ssh/vcenter-appliance/README.md @@ -1,3 +1,5 @@ +[← SSH Samples](../README.md) + # VMware vCenter Server Appliance Password Management This sample manages privileged accounts on VMware vCenter Server Appliance over SSH. It can validate the appliance configuration, rotate vCenter SSO administrator passwords, keep `root` synchronized when required, and discover members of the `Administrators` group. diff --git a/samples/telnet/README.md b/samples/telnet/README.md index 3908bc9..aa812b3 100644 --- a/samples/telnet/README.md +++ b/samples/telnet/README.md @@ -1,3 +1,5 @@ +[← Samples](../README.md) + # Telnet Samples Tested custom platform scripts for managing systems over Telnet, including TN3270 for mainframe environments. diff --git a/samples/telnet/cisco-ios/README.md b/samples/telnet/cisco-ios/README.md index 2b0ae29..d7ab7a8 100644 --- a/samples/telnet/cisco-ios/README.md +++ b/samples/telnet/cisco-ios/README.md @@ -1,3 +1,5 @@ +[← Telnet Samples](../README.md) + # Cisco IOS Password Management (Telnet) This sample manages local Cisco IOS credentials over Telnet. It can validate accounts, change local user passwords, and rotate the device's enable password while preserving the existing password style and privilege context. diff --git a/samples/telnet/racf-tn3270/README.md b/samples/telnet/racf-tn3270/README.md index 05f6c02..2287cf9 100644 --- a/samples/telnet/racf-tn3270/README.md +++ b/samples/telnet/racf-tn3270/README.md @@ -1,3 +1,5 @@ +[← Telnet Samples](../README.md) + # IBM RACF Password Management (TN3270) This sample manages RACF passwords through a TN3270 session to a z/OS logon screen. It validates logon for both service and managed accounts and changes passwords by issuing a RACF `ALU ... PASSWORD(...) NOEXPIRED` command. From e9fdf891115bcfdaccb566dd1b38d7ef3fda7a51 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:13:37 -0600 Subject: [PATCH 10/13] Expand SSH and HTTP tutorials to include ChangePassword Both tutorials now walk the reader through a complete platform with CheckSystem, CheckPassword, and ChangePassword instead of deferring ChangePassword to 'look at a sample'. The SSH tutorial teaches the interactive Send/Receive pattern with sudo passwd. The HTTP tutorial teaches admin-authenticated PUT to a password endpoint. --- docs/tutorials/your-first-http-script.md | 150 +++++++++++-- docs/tutorials/your-first-ssh-script.md | 264 +++++++++++++++++++++-- 2 files changed, 386 insertions(+), 28 deletions(-) diff --git a/docs/tutorials/your-first-http-script.md b/docs/tutorials/your-first-http-script.md index dd12f28..b1db85a 100644 --- a/docs/tutorials/your-first-http-script.md +++ b/docs/tutorials/your-first-http-script.md @@ -2,16 +2,17 @@ # Your First HTTP Script -By the end of this tutorial, you will have a working custom platform script that connects to a REST API over HTTP or HTTPS, verifies API connectivity with `CheckSystem`, and validates managed account credentials with `CheckPassword`. +By the end of this tutorial, you will have a working custom platform script that connects to a REST API over HTTP or HTTPS, verifies API connectivity with `CheckSystem`, validates managed account credentials with `CheckPassword`, and rotates the password with `ChangePassword`. ## What You'll Build -You will build a minimal custom platform script with two operations: +You will build a minimal custom platform script with three operations: - `CheckSystem` — makes an authenticated HTTP request to verify the service account can reach the API. - `CheckPassword` — makes an authenticated HTTP request to verify the managed account credentials are valid. +- `ChangePassword` — authenticates as the service account and sends a password-update request for the managed account. -This is intentionally small. It is the quickest way to get from zero to a working HTTP platform before you add more advanced request flows. +This is intentionally small. It is the quickest way to get from zero to a working HTTP platform that can test connectivity, validate credentials, and perform a basic password rotation. ## Prerequisites @@ -264,9 +265,85 @@ What changed: Just like `CheckSystem`, the final combined script below uses `UseSsl` to choose HTTPS or HTTP for this operation too. -## Step 5: The Complete Script +## Step 5: Add ChangePassword -Here is the full script with both operations in one file: +`ChangePassword` is the operation SPP uses when it rotates a managed account password. For a REST API, that usually means the service account authenticates first and then sends an administrative request to update the managed account's password. + +Unlike `CheckPassword`, which authenticates **as the managed account** to prove the existing password works, `ChangePassword` usually authenticates **as the service account** because changing another user's password normally requires admin privileges. + +Add this operation: + +```json +"ChangePassword": { + "Parameters": [ + { "Address": { "Type": "String", "Required": true } }, + { "FuncUsername": { "Type": "String", "Required": true } }, + { "FuncPassword": { "Type": "Secret", "Required": true } }, + { "AccountUserName": { "Type": "String", "Required": true } }, + { "NewPassword": { "Type": "Secret", "Required": true } }, + { "UseSsl": { "Type": "Boolean", "Required": false, "DefaultValue": true } } + ], + "Do": [ + { + "Condition": { + "If": "UseSsl", + "Then": { "Do": [{ "BaseAddress": { "Address": "https://%Address%" } }] }, + "Else": { "Do": [{ "BaseAddress": { "Address": "http://%Address%" } }] } + } + }, + { "NewHttpRequest": { "ObjectName": "ChangeRequest" } }, + { "HttpAuth": { "RequestObjectName": "ChangeRequest", "Type": "Basic", "Credentials": { "Login": "%FuncUsername%", "Password": "%FuncPassword%" } } }, + { + "Request": { + "RequestObjectName": "ChangeRequest", + "ResponseObjectName": "ChangeResponse", + "Verb": "PUT", + "Url": "/api/users/%AccountUserName%/password", + "Content": { + "ContentType": "application/json", + "Body": "{\"password\": \"%NewPassword%\"}" + } + } + }, + { + "Condition": { + "If": "ChangeResponse.StatusCode.ToString() == \"200\" || ChangeResponse.StatusCode.ToString() == \"204\"", + "Then": { "Do": [{ "Return": { "Value": true } }] }, + "Else": { "Do": [{ "Throw": { "Value": "ChangePassword failed: HTTP %{ChangeResponse.StatusCode.ToString()}%" } }] } + } + } + ] +} +``` + +Key points: + +- `FuncUsername` / `FuncPassword` are the service-account credentials used for the admin API call. +- `AccountUserName` identifies the managed account whose password SPP is rotating. +- `NewPassword` is the new password value generated by SPP and passed into the operation. +- This simplified admin-driven example does **not** send `AccountPassword`. If your API requires the old password too, add `AccountPassword` and include it in the request body. + +The `Do` block follows the standard HTTP password-change pattern: + +1. Choose `https://` or `http://` with `UseSsl`. +2. Create a new request object. +3. Authenticate the request with Basic Auth using the service account. +4. Send a `PUT` request to `/api/users/%AccountUserName%/password`. +5. Treat `200` or `204` as success and throw a clear error for anything else. + +The JSON request body is constructed inline as a string: + +```json +"Body": "{\"password\": \"%NewPassword%\"}" +``` + +The `%NewPassword%` variable is expanded before the request is sent, so the API receives real JSON such as `{"password": "N3wP@ssw0rd!"}`. Many APIs use a slightly different shape, such as including both the old and new password in the body. + +Some APIs use `POST` instead of `PUT`, or require a different body format. The pattern stays the same: authenticate as the admin, send the password-change request, and verify the success status code. + +## Step 6: The Complete Script + +Here is the full script with all three operations in one file: ```json { @@ -377,13 +454,53 @@ Here is the full script with both operations in one file: } } ] + }, + "ChangePassword": { + "Parameters": [ + { "Address": { "Type": "String", "Required": true } }, + { "FuncUsername": { "Type": "String", "Required": true } }, + { "FuncPassword": { "Type": "Secret", "Required": true } }, + { "AccountUserName": { "Type": "String", "Required": true } }, + { "NewPassword": { "Type": "Secret", "Required": true } }, + { "UseSsl": { "Type": "Boolean", "Required": false, "DefaultValue": true } } + ], + "Do": [ + { + "Condition": { + "If": "UseSsl", + "Then": { "Do": [{ "BaseAddress": { "Address": "https://%Address%" } }] }, + "Else": { "Do": [{ "BaseAddress": { "Address": "http://%Address%" } }] } + } + }, + { "NewHttpRequest": { "ObjectName": "ChangeRequest" } }, + { "HttpAuth": { "RequestObjectName": "ChangeRequest", "Type": "Basic", "Credentials": { "Login": "%FuncUsername%", "Password": "%FuncPassword%" } } }, + { + "Request": { + "RequestObjectName": "ChangeRequest", + "ResponseObjectName": "ChangeResponse", + "Verb": "PUT", + "Url": "/api/users/%AccountUserName%/password", + "Content": { + "ContentType": "application/json", + "Body": "{\"password\": \"%NewPassword%\"}" + } + } + }, + { + "Condition": { + "If": "ChangeResponse.StatusCode.ToString() == \"200\" || ChangeResponse.StatusCode.ToString() == \"204\"", + "Then": { "Do": [{ "Return": { "Value": true } }] }, + "Else": { "Do": [{ "Throw": { "Value": "ChangePassword failed: HTTP %{ChangeResponse.StatusCode.ToString()}%" } }] } + } + } + ] } } ``` -This combined version is still minimal, but it shows the full HTTP request and response pattern you will reuse in more advanced scripts. +This combined version is still minimal, but it shows the full HTTP request and response patterns you will reuse in more advanced scripts. -## Step 6: Validate and Upload +## Step 7: Validate and Upload Validate the script locally first, then create the custom platform in SPP: @@ -394,7 +511,7 @@ New-SafeguardCustomPlatform -Name "My First HTTP Platform" -ScriptFile ".\MyFirs If validation fails, fix the JSON before you upload anything. -## Step 7: Create a Test Asset and Account +## Step 8: Create a Test Asset and Account Once the platform exists, create a test asset and a test account: @@ -404,19 +521,20 @@ New-SafeguardAssetAccount "api.example.com" "testuser" Set-SafeguardAssetAccountPassword -AssetToUse "api.example.com" -AccountToUse "testuser" ``` -In this example, `admin` is the service account used by `CheckSystem` and `testuser` is the managed account you will verify with `CheckPassword`. +In this example, `admin` is the service account used by `CheckSystem` and `ChangePassword`, and `testuser` is the managed account validated by `CheckPassword` and updated by `ChangePassword`. -## Step 8: Test It +## Step 9: Test It -Now run both tests and inspect the task log output: +Now run the connectivity check, the account-password check, and finally a password change while you inspect the task log output: ```powershell Test-SafeguardAsset "api.example.com" -ExtendedLogging Test-SafeguardAssetAccountPassword "api.example.com" "testuser" -ExtendedLogging +Invoke-SafeguardAssetAccountPasswordChange -AssetToUse "api.example.com" -AccountToUse "testuser" Get-SafeguardTaskLog ``` -Start with `Test-SafeguardAsset`. If `CheckSystem` fails, fix connectivity or service-account issues before you test `CheckPassword`. +Start with `Test-SafeguardAsset`. If `CheckSystem` fails, fix connectivity or service-account issues before you test `CheckPassword`. Once both read-only checks work, run `Invoke-SafeguardAssetAccountPasswordChange` to exercise `ChangePassword`. ## What Happens When It Fails @@ -424,6 +542,7 @@ When an HTTP platform fails, start with the status code and connection details: - SSL certificate errors — add `"IgnoreServerCertAuthentication": true` to the `Request` while developing against self-signed certificates, then remove or disable it for production use. - `401 Unauthorized` — the credentials are wrong or the authentication type does not match what the API expects. +- `403 Forbidden` — the service account authenticated, but it does not have permission to change another user's password. - `404 Not Found` — the URL path is wrong. Verify the endpoint in the API documentation. - Connection refused — check the address and confirm whether the target expects HTTPS or HTTP. - Timeout — there may be a network access issue or the API may be slow. If needed, extend the script later with timeout handling. @@ -434,16 +553,19 @@ During development, always run tests with `-ExtendedLogging` and review `Get-Saf The example uses generic placeholder endpoints. To make it work with a real target: -- Change `/api/status` and `/api/users/me` to the actual endpoints your API exposes. +- Change `/api/status`, `/api/users/me`, and `/api/users/%AccountUserName%/password` to the actual endpoints your API exposes. - Switch the authentication type if needed, such as Bearer tokens or OAuth2. See the [HTTP Platforms Guide](../guides/http-platforms.md). - Add custom parameters for API-specific values such as a base path, tenant ID, or API version. +- Adjust the password-change verb or request body if your API uses `POST`, expects the old password, or wraps the new password in a different JSON structure. - Handle pagination, token exchange, or multi-step authentication flows for more complex APIs. ## Next Steps Once this minimal script works, you can build on it: -- Add `ChangePassword` by following the HTTP patterns in the [HTTP Platforms Guide](../guides/http-platforms.md). +- Add account discovery so SPP can onboard accounts automatically from the target system. - Implement Bearer token authentication for OAuth2-based APIs. +- Add API-specific parameters such as a tenant ID, API version, or base path. +- Adapt `ChangePassword` to APIs that require the current password or a follow-up verification call. - Explore the real-world [`WordPressHttp.json`](../../samples/http/wordpress/WordPressHttp.json) sample. - Read the [Operations Reference](../reference/operations.md). diff --git a/docs/tutorials/your-first-ssh-script.md b/docs/tutorials/your-first-ssh-script.md index 74c7611..c33d7cb 100644 --- a/docs/tutorials/your-first-ssh-script.md +++ b/docs/tutorials/your-first-ssh-script.md @@ -2,14 +2,15 @@ # Your First SSH Script -By the end of this tutorial, you will have a working custom platform script that connects to a Linux host over SSH, verifies that the service account can log in with `CheckSystem`, and validates a managed account password with `CheckPassword`. +By the end of this tutorial, you will have a working custom platform script that connects to a Linux host over SSH, verifies that the service account can log in with `CheckSystem`, validates a managed account password with `CheckPassword`, and rotates the password with `ChangePassword`. ## What You'll Build -You will build a minimal custom platform script with two operations: +You will build a minimal custom platform script with three operations: - `CheckSystem` — connects over SSH and verifies the service account can log in. - `CheckPassword` — connects over SSH and attempts login with the managed account credentials. +- `ChangePassword` — connects over SSH as the service account and changes the managed account password through an interactive `passwd` flow. This is intentionally small. It is the quickest way to get from zero to a working SSH platform before you add more advanced features. @@ -168,9 +169,157 @@ Here is what is new: This is a deliberately simple pattern. Production-ready Linux platforms often use helper functions, imports, delegation logic, and stronger error handling, but this direct-login version is ideal for learning the basics. -## Step 5: The Complete Script +## Step 5: Add ChangePassword -Here is the full script with both operations in one file: +`ChangePassword` is the operation SPP calls when it actually rotates the managed account password. `CheckPassword` only proves the current password works. `ChangePassword` is the operation that changes it on the target system. + +For this beginner walkthrough, use the service account and an interactive `sudo passwd` session. That keeps the example simple and also teaches an important scripting pattern: `Send` a command, `Receive` a prompt, and continue step by step. + +Start by adding the `Parameters` block: + +```json +"ChangePassword": { + "Parameters": [ + { "Address": { "Type": "String", "Required": true } }, + { "Port": { "Type": "Integer", "Required": false, "DefaultValue": 22 } }, + { "Timeout": { "Type": "Integer", "Required": false, "DefaultValue": 20 } }, + { "FuncUserName": { "Type": "String", "Required": true } }, + { "FuncPassword": { "Type": "Secret", "Required": false } }, + { "AccountUserName": { "Type": "String", "Required": true } }, + { "NewPassword": { "Type": "Secret", "Required": true } }, + { "CheckHostKey": { "Type": "Boolean", "Required": false, "DefaultValue": true } }, + { "HostKey": { "Type": "String", "Required": false } }, + { "RequestTerminal": { "Type": "Boolean", "Required": false, "DefaultValue": true } } + ], + "Do": [] +} +``` + +Most of these parameters are the same as `CheckSystem` because this operation connects as the service account, not as the managed account. The important additions are: + +- `AccountUserName` — the account whose password you want to change. +- `NewPassword` — the new password value that SPP generated and passed into the script. +- There is no `AccountPassword` in this learning example because the service account does the work through `sudo passwd`. In some platforms you may need the current password too, but not in this tutorial. + +Now build the `Do` block a few steps at a time. + +First, connect as the service account and start the password change command: + +```json +"Do": [ + { + "Connect": { + "ConnectionObjectName": "Global:SshConnection", + "Type": "Ssh", + "NetworkAddress": "%Address%", + "Port": "%Port%", + "Login": "%FuncUserName%", + "Password": "%FuncPassword::$%", + "RequestTerminal": "%RequestTerminal%", + "CheckHostKey": "%CheckHostKey%", + "Hostkey": "%HostKey::$%", + "Timeout": "%Timeout%" + } + }, + { + "Send": { + "ConnectionObjectName": "SshConnection", + "Buffer": "sudo passwd %AccountUserName%" + } + } +] +``` + +This reuses the same SSH connection pattern from `CheckSystem`, but instead of disconnecting immediately, it sends `sudo passwd` for the target account. `RequestTerminal` stays `true` because `passwd` is interactive and expects a terminal. + +Next, wait for the first password prompt: + +```json +{ + "Receive": { + "ConnectionObjectName": "SshConnection", + "BufferName": "PromptResult", + "ExpectRegex": "([Nn]ew.*[Pp]assword:)|([Pp]assword:)" + } +} +``` + +`Receive` pauses the script until the remote system writes text that matches the regular expression. This pattern is intentionally flexible: + +- `([Nn]ew.*[Pp]assword:)` matches prompts such as `New password:` or `New UNIX password:`. +- `([Pp]assword:)` is a fallback for systems that use a simpler password prompt. + +When the prompt appears, send the new password: + +```json +{ + "Send": { + "ConnectionObjectName": "SshConnection", + "Buffer": "%NewPassword%", + "ContainsSecret": true + } +} +``` + +`ContainsSecret: true` tells the engine that the buffer contains sensitive data. That keeps the password masked in logs instead of echoing it back in plain text. + +Then wait for the confirmation prompt and send the password again: + +```json +{ + "Receive": { + "ConnectionObjectName": "SshConnection", + "BufferName": "PromptResult", + "ExpectRegex": "([Rr]etype|[Rr]e-enter|[Cc]onfirm).*[Pp]assword:" + } +}, +{ + "Send": { + "ConnectionObjectName": "SshConnection", + "Buffer": "%NewPassword%", + "ContainsSecret": true + } +} +``` + +The second `Receive` looks for common confirmation prompts such as `Retype new password:`, `Re-enter new password:`, or `Confirm password:`. After that prompt appears, the script sends the same `NewPassword` value again. + +Finally, wait for success, then disconnect and return: + +```json +{ + "Receive": { + "ConnectionObjectName": "SshConnection", + "BufferName": "ChangeResult", + "ExpectRegex": "(updated successfully)|(\\$\\s*$)" + } +}, +{ + "Disconnect": { "ConnectionObjectName": "SshConnection" } +}, +{ + "Return": { "Value": true } +} +``` + +This last `Receive` checks the result of the password change. The regex allows two common success signals: + +- `updated successfully` — many Linux `passwd` implementations print a success message like `password updated successfully`. +- `(\\$\\s*$)` — some systems return you straight to a shell prompt, so the expression also allows a trailing `$` prompt. + +That gives you the full interactive flow: + +1. Send the `sudo passwd` command. +2. Wait for the first prompt. +3. Send `NewPassword`. +4. Wait for the confirmation prompt. +5. Send `NewPassword` again. +6. Wait for success. +7. Disconnect and return `true`. + +## Step 6: The Complete Script + +Here is the full script with all three operations in one file: ```json { @@ -245,13 +394,90 @@ Here is the full script with both operations in one file: "Return": { "Value": true } } ] + }, + "ChangePassword": { + "Parameters": [ + { "Address": { "Type": "String", "Required": true } }, + { "Port": { "Type": "Integer", "Required": false, "DefaultValue": 22 } }, + { "Timeout": { "Type": "Integer", "Required": false, "DefaultValue": 20 } }, + { "FuncUserName": { "Type": "String", "Required": true } }, + { "FuncPassword": { "Type": "Secret", "Required": false } }, + { "AccountUserName": { "Type": "String", "Required": true } }, + { "NewPassword": { "Type": "Secret", "Required": true } }, + { "CheckHostKey": { "Type": "Boolean", "Required": false, "DefaultValue": true } }, + { "HostKey": { "Type": "String", "Required": false } }, + { "RequestTerminal": { "Type": "Boolean", "Required": false, "DefaultValue": true } } + ], + "Do": [ + { + "Connect": { + "ConnectionObjectName": "Global:SshConnection", + "Type": "Ssh", + "NetworkAddress": "%Address%", + "Port": "%Port%", + "Login": "%FuncUserName%", + "Password": "%FuncPassword::$%", + "RequestTerminal": "%RequestTerminal%", + "CheckHostKey": "%CheckHostKey%", + "Hostkey": "%HostKey::$%", + "Timeout": "%Timeout%" + } + }, + { + "Send": { + "ConnectionObjectName": "SshConnection", + "Buffer": "sudo passwd %AccountUserName%" + } + }, + { + "Receive": { + "ConnectionObjectName": "SshConnection", + "BufferName": "PromptResult", + "ExpectRegex": "([Nn]ew.*[Pp]assword:)|([Pp]assword:)" + } + }, + { + "Send": { + "ConnectionObjectName": "SshConnection", + "Buffer": "%NewPassword%", + "ContainsSecret": true + } + }, + { + "Receive": { + "ConnectionObjectName": "SshConnection", + "BufferName": "PromptResult", + "ExpectRegex": "([Rr]etype|[Rr]e-enter|[Cc]onfirm).*[Pp]assword:" + } + }, + { + "Send": { + "ConnectionObjectName": "SshConnection", + "Buffer": "%NewPassword%", + "ContainsSecret": true + } + }, + { + "Receive": { + "ConnectionObjectName": "SshConnection", + "BufferName": "ChangeResult", + "ExpectRegex": "(updated successfully)|(\\$\\s*$)" + } + }, + { + "Disconnect": { "ConnectionObjectName": "SshConnection" } + }, + { + "Return": { "Value": true } + } + ] } } ``` If you compare this with a production-ready sample such as [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json), you will notice that the sample adds reusable functions, richer validation, better error handling, and support for more SSH scenarios. Start with the minimal version here, then grow into those patterns later. -## Step 6: Validate and Upload +## Step 7: Validate and Upload Validate the script locally first, then create the custom platform in SPP: @@ -265,7 +491,7 @@ New-SafeguardCustomPlatform -Name "My First SSH Platform" -ScriptFile ".\MyFirst Validation is your first checkpoint. If `Test-SafeguardCustomPlatformScript` fails, fix the JSON before you upload anything. -## Step 7: Create a Test Asset and Account +## Step 8: Create a Test Asset and Account Once the platform exists, create a test asset and a test account: @@ -277,13 +503,15 @@ Set-SafeguardAssetAccountPassword -AssetToUse "10.0.0.1" -AccountToUse "testuser In this example: -- The asset uses `root` as the service account for `CheckSystem`. -- `testuser` is the managed account you will verify with `CheckPassword`. +- The asset uses `root` as the service account for `CheckSystem` and `ChangePassword`. +- `testuser` is the managed account you will verify with `CheckPassword` and rotate with `ChangePassword`. - `Set-SafeguardAssetAccountPassword` securely prompts you for the managed account password. -## Step 8: Test It +Because `ChangePassword` uses `sudo passwd`, make sure the service account is allowed to change the target account's password. For a first lab, using `root` as the service account is the simplest setup. -Now run both tests and inspect the task log output: +## Step 9: Test It + +Now run all three operations and inspect the task log output: ```powershell # Test connectivity (CheckSystem) @@ -292,11 +520,17 @@ Test-SafeguardAsset "10.0.0.1" -ExtendedLogging # Test password verification (CheckPassword) Test-SafeguardAssetAccountPassword "10.0.0.1" "testuser" -ExtendedLogging +# Test password rotation (ChangePassword) +Invoke-SafeguardAssetAccountPasswordChange "10.0.0.1" "testuser" + +# Verify the new password now works +Test-SafeguardAssetAccountPassword "10.0.0.1" "testuser" -ExtendedLogging + # Review the logs Get-SafeguardTaskLog ``` -Start with `CheckSystem`. If that fails, fix connectivity or service-account issues before you test `CheckPassword`. +Start with `CheckSystem`. If that fails, fix connectivity or service-account issues first. Then confirm `CheckPassword` works before you test `ChangePassword`, because rotation depends on the same SSH path plus the interactive prompt handling. ## What Happens When It Fails @@ -305,6 +539,7 @@ When your first script does not work, start with the simplest explanation: - Connection timeout — verify network access to the host and confirm the SSH port is open. - Host key mismatch — accept the correct host key or temporarily set `CheckHostKey` to `false` while testing. - Authentication failure — verify the service account credentials on the asset and the managed account password on the account. +- Prompt mismatch during `ChangePassword` — review the `Receive` regex values and compare them with the exact `passwd` prompts on your target Linux system. - Script validation error — check your JSON syntax, parameter names, and commas carefully. During development, always run tests with `-ExtendedLogging` and review `Get-SafeguardTaskLog` so you can see exactly where the operation failed. @@ -313,7 +548,8 @@ During development, always run tests with `-ExtendedLogging` and review `Get-Saf Once this minimal script works, you are ready to extend it: -- Add `ChangePassword` by following the SSH patterns in the [SSH platforms guide](../guides/ssh-platforms.md). -- Add error handling with `Try` / `Catch` blocks. -- Explore the full [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json) sample for production-ready patterns such as imports, error handling, and `sudo`-based flows. +- Add error handling with `Try` / `Catch` blocks and clearer failure messages. +- Add `DiscoverAccounts` so SPP can find local accounts automatically. +- Explore the full [`GenericLinux.json`](../../samples/ssh/generic-linux/GenericLinux.json) sample for production-ready patterns such as imports, helper functions, stronger error handling, and more flexible `sudo` flows. +- Read the [SSH platforms guide](../guides/ssh-platforms.md) for more SSH patterns beyond this beginner exercise. - Read the [Operations Reference](../reference/operations.md) to see the other operations you can implement. From 6d7a491db37ce113ade437b63bdfe87001eabab3 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:21:34 -0600 Subject: [PATCH 11/13] Rationalize capstone tutorial with expanded first-script tutorials The first-script tutorials now cover ChangePassword, so the capstone no longer re-teaches CheckSystem/CheckPassword/ChangePassword from scratch. Instead it focuses on what's new: DiscoverAccounts, reusable Functions, Try/Catch error handling, and Status messages. Uses correct command syntax consistent with the rest of the documentation. --- docs/tutorials/README.md | 6 +- .../tutorials/building-a-complete-platform.md | 296 +++++++++++------- 2 files changed, 184 insertions(+), 118 deletions(-) diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 32267c1..4418e2f 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -6,10 +6,10 @@ Step-by-step walkthroughs that teach you how to build custom platform scripts fr | Tutorial | Description | | --- | --- | -| [Your First SSH Script](your-first-ssh-script.md) | Build a minimal SSH platform with `CheckSystem` and `CheckPassword`. | -| [Your First HTTP Script](your-first-http-script.md) | Build a minimal REST API platform with Basic Auth. | +| [Your First SSH Script](your-first-ssh-script.md) | Build a complete SSH platform with `CheckSystem`, `CheckPassword`, and `ChangePassword`. | +| [Your First HTTP Script](your-first-http-script.md) | Build a complete REST API platform with Basic Auth including password rotation. | | [Your First Form Script](your-first-form-script.md) | Manage passwords on a web portal using HTML form submission. | -| [Building a Complete Platform](building-a-complete-platform.md) | Go from a minimal script to a full-featured platform with multiple operations. | +| [Building a Complete Platform](building-a-complete-platform.md) | Add discovery, reusable functions, error handling, and status messages to a working platform. | ## Before You Start diff --git a/docs/tutorials/building-a-complete-platform.md b/docs/tutorials/building-a-complete-platform.md index b0a2034..1663759 100644 --- a/docs/tutorials/building-a-complete-platform.md +++ b/docs/tutorials/building-a-complete-platform.md @@ -2,170 +2,236 @@ # Building a Complete Platform -This tutorial takes you from a minimal custom platform script to a full-featured one with multiple operations. By the end, you'll have a platform that can check connectivity, validate passwords, change passwords, and discover accounts — all over SSH. +This tutorial picks up where [Your First SSH Script](your-first-ssh-script.md) left off. You already have a working platform with `CheckSystem`, `CheckPassword`, and `ChangePassword`. Now you'll add account discovery, reusable functions, error handling, and structured logging to turn it into a production-quality platform. ## Prerequisites -- Completed [Your First SSH Script](your-first-ssh-script.md) (or equivalent knowledge) -- A Linux target with SSH access and at least two user accounts +- Completed [Your First SSH Script](your-first-ssh-script.md) — you should have a working script with `CheckSystem`, `CheckPassword`, and `ChangePassword` +- A Linux target with SSH access and at least two local user accounts - SPP with `safeguard-ps` installed -## What You'll Build +## What You'll Add -A complete Linux SSH platform with these operations: - -| Operation | Purpose | +| Capability | What it does | | --- | --- | -| `CheckSystem` | Verify the service account can connect | -| `CheckPassword` | Validate a managed account's password | -| `ChangePassword` | Rotate a managed account's password | -| `DiscoverAccounts` | Find local accounts on the system | +| `DiscoverAccounts` | Automatically finds local accounts on the system | +| Reusable `Functions` | Eliminates duplicated connection logic across operations | +| `Try`/`Catch` error handling | Makes operations resilient instead of crashing on unexpected output | +| Status messages | Reports progress back to SPP during long-running operations | + +## Step 1: Add DiscoverAccounts -## Step 1: Start with CheckSystem and CheckPassword +Account discovery lets SPP automatically find accounts on the managed system instead of requiring manual entry. The operation connects, queries the system for accounts, and reports each one back using `WriteDiscoveredAccount`. -If you followed the first SSH tutorial, you already have a script with these two operations. If not, start with: +Because discovery only reads data (no interactive prompts), it works well with `ExecuteCommand` in batch mode (`RequestTerminal: false`): ```json -{ - "Id": "MyCompletePlatform", - "BackEnd": "Scriptable", - "CheckSystem": { - "Parameters": [ - { "Address": "" }, - { "Port": "22" }, - { "FuncUserName": "" }, - { "FuncPassword": "" } - ], - "Do": [ - { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "RequestTerminal": true } }, - { "Send": { "Text": "echo connected\n" } }, - { "Receive": { "Regex": "connected" } }, - { "Disconnect": {} } - ] - }, - "CheckPassword": { - "Parameters": [ - { "Address": "" }, - { "Port": "22" }, - { "AccountUserName": "" }, - { "AccountPassword": "" } - ], - "Do": [ - { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%AccountUserName%", "Password": "%AccountPassword%", "RequestTerminal": true } }, - { "Disconnect": {} } - ] - } +"DiscoverAccounts": { + "Parameters": [ + { "Address": { "Type": "String", "Required": true } }, + { "Port": { "Type": "Integer", "Required": false, "DefaultValue": 22 } }, + { "Timeout": { "Type": "Integer", "Required": false, "DefaultValue": 20 } }, + { "FuncUserName": { "Type": "String", "Required": true } }, + { "FuncPassword": { "Type": "Secret", "Required": false } }, + { "CheckHostKey": { "Type": "Boolean", "Required": false, "DefaultValue": true } }, + { "HostKey": { "Type": "String", "Required": false } } + ], + "Do": [ + { + "Connect": { + "ConnectionObjectName": "Global:SshConnection", + "Type": "Ssh", + "NetworkAddress": "%Address%", + "Port": "%Port%", + "Login": "%FuncUserName%", + "Password": "%FuncPassword::$%", + "RequestTerminal": false, + "CheckHostKey": "%CheckHostKey%", + "Hostkey": "%HostKey::$%", + "Timeout": "%Timeout%" + } + }, + { + "ExecuteCommand": { + "ConnectionObjectName": "SshConnection", + "Command": "awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {print $1}' /etc/passwd", + "ResultVariable": "Result" + } + }, + { + "Condition": { + "If": "(Result.rc != 0)", + "Then": { "Do": [ + { "Throw": { "Message": "Failed to query accounts: %{ Result.Stderr }%" } } + ] } + } + }, + { + "ForEach": { + "Item": "acct", + "In": "%{ Result.Stdout.Split('\\n') }%", + "Do": [ + { + "Condition": { + "If": "acct != ''", + "Then": { "Do": [ + { "WriteDiscoveredAccount": { "AccountName": "%acct%" } } + ] } + } + } + ] + } + }, + { + "Disconnect": { "ConnectionObjectName": "SshConnection" } + } + ] } ``` -Upload and test: +Key points: + +- **`RequestTerminal: false`** — batch mode sends a command and captures stdout/stderr directly, without needing `Send`/`Receive` prompt matching. +- **`ExecuteCommand`** — runs a single command and returns `Result.Stdout`, `Result.Stderr`, and `Result.rc` (exit code). +- **`ForEach`** — iterates over the split output, one account name per line. +- **`WriteDiscoveredAccount`** — reports each account to SPP. This is how discovery populates the account list. +- The `awk` filter keeps only real user accounts (UID ≥ 1000, active shell). + +Test discovery from SPP's web UI under **Asset Management > Discovery**, or trigger it with: + ```powershell -Import-SafeguardCustomPlatformScript -FilePath .\MyCompletePlatform.json -Test-SafeguardAssetConnection -AssetToUse "TestHost" -ExtendedLogging -Test-SafeguardAssetAccountPassword -AssetToUse "TestHost" -AccountToUse "testuser" -ExtendedLogging +Invoke-SafeguardAssetAccountDiscovery -AssetToUse "TestHost" ``` -## Step 2: Add ChangePassword +## Step 2: Extract Reusable Functions + +Your script now has four operations, and three of them (`CheckSystem`, `CheckPassword`, `ChangePassword`) all contain similar connection logic. This is a maintenance burden. If you need to change the connection pattern, you'd have to update it in three places. -The `ChangePassword` operation connects with the service account (which has privileges to change other users' passwords) and runs `passwd`: +Extract the common login logic into a function: ```json -"ChangePassword": { +"LoginSsh": { "Parameters": [ - { "Address": "" }, - { "Port": "22" }, - { "FuncUserName": "" }, - { "FuncPassword": "" }, - { "AccountUserName": "" }, - { "AccountPassword": "" }, - { "NewPassword": "" } + { "UserName": { "Type": "String", "Required": true } }, + { "Password": { "Type": "Secret", "Required": false } } ], "Do": [ - { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "RequestTerminal": true } }, - { "Send": { "Text": "sudo passwd %AccountUserName%\n" } }, - { "Receive": { "Regex": "[Nn]ew password:|[Pp]assword:" } }, - { "Send": { "Text": "%NewPassword%\n" } }, - { "Receive": { "Regex": "[Rr]etype|[Rr]e-enter|[Cc]onfirm|[Nn]ew password:" } }, - { "Send": { "Text": "%NewPassword%\n" } }, - { "Receive": { "Regex": "successfully|updated|\\$|#" } }, - { "Disconnect": {} } + { + "Connect": { + "ConnectionObjectName": "Global:SshConnection", + "Type": "Ssh", + "NetworkAddress": "%Address%", + "Port": "%Port%", + "Login": "%UserName%", + "Password": "%Password::$%", + "RequestTerminal": "%RequestTerminal%", + "CheckHostKey": "%CheckHostKey%", + "Hostkey": "%HostKey::$%", + "Timeout": "%Timeout%" + } + }, + { "Return": { "Value": true } } ] } ``` -Key points: -- `ChangePassword` connects with the **service account** (`FuncUserName`/`FuncPassword`), not the managed account. -- SPP provides `NewPassword` — you don't generate it yourself. -- The `Receive` patterns must match your target's `passwd` prompts. +Then simplify each operation to call the function: -Test with: -```powershell -Invoke-SafeguardAssetAccountPasswordChange -AssetToUse "TestHost" -AccountToUse "testuser" +```json +"CheckSystem": { + "Parameters": [ ... ], + "Do": [ + { "Function": { "Name": "LoginSsh", "Parameters": ["%FuncUserName%", "%FuncPassword%"] } }, + { "Disconnect": { "ConnectionObjectName": "SshConnection" } }, + { "Return": { "Value": true } } + ] +} ``` -## Step 3: Add DiscoverAccounts +Functions are defined as top-level keys in the script (alongside operations like `CheckSystem`). SPP distinguishes them from operations because they aren't in the list of recognized operation names. Functions can access variables from the calling operation's `Parameters` that are marked `Global:` or passed explicitly. + +## Step 3: Add Error Handling with Try/Catch -The `DiscoverAccounts` operation connects and lists local accounts, then reports each one back to SPP using `WriteDiscoveredAccount`: +Without error handling, any unexpected output (a different prompt format, a timeout, an unexpected error message) causes the entire operation to fail with a generic error. Wrapping critical sections in `Try`/`Catch` gives you control over failure behavior: ```json -"DiscoverAccounts": { - "Parameters": [ - { "Address": "" }, - { "Port": "22" }, - { "FuncUserName": "" }, - { "FuncPassword": "" } - ], +{ + "Try": { + "Do": [ + { "Send": { "ConnectionObjectName": "SshConnection", "Buffer": "sudo passwd %AccountUserName%" } }, + { "Receive": { "ConnectionObjectName": "SshConnection", "BufferName": "PromptResult", "ExpectRegex": "([Nn]ew.*[Pp]assword:)|([Pp]assword:)" } }, + { "Send": { "ConnectionObjectName": "SshConnection", "Buffer": "%NewPassword%", "ContainsSecret": true } }, + { "Receive": { "ConnectionObjectName": "SshConnection", "BufferName": "PromptResult", "ExpectRegex": "([Rr]etype|[Rr]e-enter|[Cc]onfirm).*[Pp]assword:" } }, + { "Send": { "ConnectionObjectName": "SshConnection", "Buffer": "%NewPassword%", "ContainsSecret": true } }, + { "Receive": { "ConnectionObjectName": "SshConnection", "BufferName": "ChangeResult", "ExpectRegex": "(updated successfully)|(\\$\\s*$)|(#\\s*$)" } } + ], + "Catch": { + "Do": [ + { "Log": { "Text": "ChangePassword failed: %{Exception.Message}%" } }, + { "Disconnect": { "ConnectionObjectName": "SshConnection" } }, + { "Return": { "Value": false } } + ] + } + } +} +``` + +The `Catch` block runs when any command in the `Try` block throws — whether from a `Receive` timeout, a connection drop, or an explicit `Throw`. This lets you: + +- Log the failure reason for troubleshooting +- Disconnect cleanly instead of leaving orphaned sessions +- Return `false` to signal the operation failed without crashing the entire task + +## Step 4: Add Status Messages + +For operations that take time (connecting, changing passwords, running discovery), status messages keep the SPP UI informed about progress: + +```json +{ "Status": { "Type": "Changing", "Percent": 10, "Message": { "Name": "ConnectingToHost", "Parameters": ["%Address%"] } } } +``` + +Add these at key points in your operations: + +```json +"ChangePassword": { "Do": [ - { "Connect": { "Address": "%Address%", "Port": "%Port%", "UserName": "%FuncUserName%", "Password": "%FuncPassword%", "RequestTerminal": false } }, - { "ExecuteCommand": { "Command": "awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {print $1}' /etc/passwd", "Output": "accounts" } }, - { "Split": { "Text": "%accounts.StdOut%", "Delimiter": "\n", "Output": "accountList" } }, - { - "ForEach": { - "Item": "acct", - "In": "%accountList%", - "Do": [ - { - "Condition": { - "If": "acct != ''", - "Then": { - "Do": [ - { "WriteDiscoveredAccount": { "AccountName": "%acct%" } } - ] - } - } - } - ] - } - }, - { "Disconnect": {} } + { "Status": { "Type": "Changing", "Percent": 10, "Message": { "Name": "ConnectingToHost", "Parameters": ["%Address%"] } } }, + { "Function": { "Name": "LoginSsh", "Parameters": ["%FuncUserName%", "%FuncPassword%"] } }, + { "Status": { "Type": "Changing", "Percent": 40, "Message": { "Name": "ChangingPassword", "Parameters": ["%AccountUserName%"] } } }, + ...password change logic..., + { "Status": { "Type": "Changing", "Percent": 90, "Message": { "Name": "DisconnectingFromHost" } } }, + { "Disconnect": { "ConnectionObjectName": "SshConnection" } }, + { "Return": { "Value": true } } ] } ``` -Key points: -- Uses `ExecuteCommand` (batch mode with `RequestTerminal: false`) instead of interactive Send/Receive. -- Filters `/etc/passwd` for real user accounts (UID >= 1000, no nologin shell). -- Calls `WriteDiscoveredAccount` once per account — this is how SPP learns about discovered accounts. +The `Percent` values give SPP a progress bar. The `Message` `Name` values are status message keys — see [Status Messages Reference](../reference/status-messages.md) for the full list of built-in keys. -## Step 4: Put It All Together +## Step 5: Verify Everything -Your complete script now has four operations. Upload the final version and verify all operations work: +Upload and run through all operations: ```powershell -# Re-upload (replaces the previous version) Import-SafeguardCustomPlatformScript -FilePath .\MyCompletePlatform.json -# Test each operation +# CheckSystem Test-SafeguardAssetConnection -AssetToUse "TestHost" -ExtendedLogging + +# CheckPassword Test-SafeguardAssetAccountPassword -AssetToUse "TestHost" -AccountToUse "testuser" -ExtendedLogging + +# ChangePassword Invoke-SafeguardAssetAccountPasswordChange -AssetToUse "TestHost" -AccountToUse "testuser" + +# DiscoverAccounts — trigger from the web UI or wait for the configured schedule ``` -Account discovery runs on a schedule configured in SPP — you can trigger it manually from the web UI under **Asset Management > Discovery**. +Review logs with `Get-SafeguardTaskLog` after each test. With error handling in place, failures will show your custom log messages instead of raw exceptions. ## What SPP Knows About Your Platform -Because your script contains these four operations, SPP automatically sets these feature flags: +Because your script contains these operations, SPP automatically derives feature flags: | Flag | Set because | | --- | --- | @@ -173,7 +239,7 @@ Because your script contains these four operations, SPP automatically sets these | `AccountPasswordFl` | `ChangePassword` is present | | `AccountDiscoveryFl` | `DiscoverAccounts` is present | -You never configure these manually — they're derived from your script. See [Feature Flags](../concepts/feature-flags.md) for the full list. +You never configure these manually — they're derived from your script content. See [Feature Flags](../concepts/feature-flags.md) for the full list. ## Next Steps @@ -181,7 +247,7 @@ From here you can extend your platform further: - **SSH key management** — Add `CheckSshKey`, `ChangeSshKey`, `DiscoverAuthorizedKeys`. See [SSH Key Management Guide](../guides/ssh-key-management.md). - **Host key discovery** — Add `DiscoverSshHostKey`. See the [generic-linux-with-discovery](../../samples/ssh/generic-linux-with-discovery/) sample. -- **Error handling** — Wrap operations in `Try`/`Catch` for resilient behavior. See [Error Handling Guide](../guides/error-handling.md). -- **Import libraries** — Use `Imports` to pull in reusable SSH functions. See [Imports Reference](../reference/imports.md). +- **Import libraries** — Use `Imports` to share functions across multiple platform scripts. See [Imports Reference](../reference/imports.md). +- **Dependent systems** — Add `UpdateDependentSystem` to propagate password changes. See the [dependent systems template](../../templates/Pattern-GenericLinuxDependentSystem.json). For a production-ready example of everything combined, study the [GenericLinuxWithSSHKeySupport](../../samples/ssh/generic-linux-ssh-keys/) sample. From 76bcedd0f61f9f86f30db0f1fee297afaffc3de7 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:55:55 -0600 Subject: [PATCH 12/13] Add visual configuration walkthrough to OneLogin JIT sample The assets/ folder contained 23 screenshots and a video showing how to configure the OneLogin JIT add-on end-to-end, but the README never referenced them. Added a 9-step Configuration Walkthrough section that embeds each screenshot with descriptions covering: uploading the platform script, understanding OneLogin roles, creating JIT assets, configuring accounts and privilege group memberships, setting up dynamic account groups, and configuring entitlements with access request policies. --- samples/http/onelogin-jit/README.md | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/samples/http/onelogin-jit/README.md b/samples/http/onelogin-jit/README.md index c25c351..1f193cc 100644 --- a/samples/http/onelogin-jit/README.md +++ b/samples/http/onelogin-jit/README.md @@ -34,6 +34,94 @@ OneLogin users and role assignments used in Safeguard JIT access workflows. 4. Configure service account, managed account(s), and JIT role mappings for `PrivilegeGroupMembership` 5. Test with `Test-SafeguardAsset -ExtendedLogging`, then exercise the access-request workflows that call `EnableAccount`, `DisableAccount`, `ElevateAccount`, and `DemoteAccount` +## Configuration Walkthrough + +The `assets/` folder contains numbered screenshots and a video (`http_oneloginjit_1.mp4`) showing the full configuration end-to-end. The steps below explain what each screenshot shows. + +### Step 1 — Upload the Platform Script + +Navigate to **Asset Management → Connect and Platforms → Custom Platforms** and click the **+** button to create a new platform. + +| Screenshot | What it shows | +| --- | --- | +| ![Navigate to Custom Platforms](assets/http_oneloginjit_5.png) | Finding the Custom Platforms section under Connect and Platforms. | +| ![New Platform dialog](assets/http_oneloginjit_6.png) | Creating the platform named "OneLogin JIT add-on" and selecting the uploaded script. | + +### Step 2 — Understand Your OneLogin Roles + +Before creating assets, identify the OneLogin roles you want to use for JIT elevation. Each role maps to a cloud application. + +| Screenshot | What it shows | +| --- | --- | +| ![OneLogin roles](assets/http_oneloginjit_10.png) | OneLogin roles (Cloud Admin - AWS, Cloud Admin - Entra, Cloud Admin - Salesforce) and their associated apps. | + +### Step 3 — Create JIT Assets + +Create one asset per role/cloud application. All assets use the same "OneLogin JIT add-on" platform and point to your OneLogin API endpoint. + +| Screenshot | What it shows | +| --- | --- | +| ![Assets list](assets/http_oneloginjit_11.png) | Three JIT assets created — one per cloud admin role — all using the OneLogin JIT add-on platform. | + +### Step 4 — Configure Accounts + +Add a service account (OAuth client credentials) and managed accounts to the JIT assets. + +| Screenshot | What it shows | +| --- | --- | +| ![Asset accounts](assets/http_oneloginjit_12.png) | The "Cloud Admin - AWS" asset with a service account and managed account (asmith-adm@oneidentity.demo). | +| ![Base asset account](assets/http_oneloginjit_7.png) | The base OneLogin asset showing the `asmith-adm` account with domain, password profile, and session request enabled. | + +### Step 5 — Set JIT Privilege Group Membership + +On each managed account's **Management** tab, configure the **JIT Privilege Group Membership** field with the OneLogin role name that should be granted during elevation. + +| Screenshot | What it shows | +| --- | --- | +| ![Privilege Group Membership](assets/http_oneloginjit_13.png) | The managed account's Management tab with "JIT Privilege Group Membership" set to "Cloud Admin - AWS". This is the role the script will assign during `ElevateAccount`. | + +### Step 6 — Create an Account Group with Dynamic Rules + +Create an account group that dynamically discovers all JIT-managed accounts using Account Rules. + +| Screenshot | What it shows | +| --- | --- | +| ![Account Rules](assets/http_oneloginjit_19.png) | The `asmith-adm` account group with rules: (Name Contains asmith-adm) AND (Asset Name Contains OneLogin JIT). | +| ![Account Group members](assets/http_oneloginjit_20.png) | The account group automatically discovers accounts across all three JIT assets. | + +### Step 7 — Configure Entitlements and Access Request Policies + +Create entitlements that tie together users, access request policies, and the JIT accounts. + +| Screenshot | What it shows | +| --- | --- | +| ![Entitlement](assets/http_oneloginjit_14.png) | The "OneLogin access (via AD)" entitlement. | +| ![Entitlement policies](assets/http_oneloginjit_15.png) | Two access request policies: "OneLogin Session" (RDP Application) and "Cloud Admin JIT" (Password). | +| ![Entitlement users](assets/http_oneloginjit_18.png) | The entitlement assigned to user Annie Smith (asmith@oneidentity.demo). | + +### Step 8 — Configure Access Request Policy Details + +Set security options and scope for the JIT password policy. + +| Screenshot | What it shows | +| --- | --- | +| ![Policy security settings](assets/http_oneloginjit_16.png) | The "OneLogin Session" policy with "Change Password After Check-In" enabled, SPS Connection Policy set, and "Asset-Based Session Access" using a Linked Account. | +| ![Policy scope](assets/http_oneloginjit_17.png) | The policy scoped to the base OneLogin asset. | +| ![JIT policy in list](assets/http_oneloginjit_21.png) | The "Cloud Admin JIT" policy (Password type) added to the entitlement. | +| ![JIT policy scope](assets/http_oneloginjit_22.png) | The Cloud Admin JIT policy scoped to the `asmith-adm` account group (dynamic). | + +### Step 9 — Verify the End-User Experience + +After configuration, the user sees all available access requests including JIT elevation targets. + +| Screenshot | What it shows | +| --- | --- | +| ![Access request view](assets/http_oneloginjit_23.png) | The end-user "New Access Request" screen showing all OneLogin JIT assets available for password checkout. | + +### Video Walkthrough + +A video demonstration is available at [`assets/http_oneloginjit_1.mp4`](assets/http_oneloginjit_1.mp4) showing the full workflow in action. + ## How It Works The script authenticates with `auth/oauth2/v2/token` using client credentials and stores the returned bearer token. For account lifecycle operations it looks up the OneLogin user by username and sends a `PUT` to `api/2/users/%UserId%` with the desired status. For role elevation and demotion it resolves each role name to a role ID, adds or removes the user with `api/2/roles/%RoleId%/users`, and repeatedly checks role membership with `api/2/roles/%RoleId%/users?name=%Username%` until the change is visible. At the end of each run it revokes the access token with `auth/oauth2/revoke`. From 606c5bb7248f0579d362f8784527b4b34965d260 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 30 May 2026 22:58:06 -0600 Subject: [PATCH 13/13] Add clickable platform script links to all sample READMEs Each sample README now has a 'Platform Script: [filename.json](./file)' link near the top, making it easy to click through to the actual script file from the documentation. Previously the filename only appeared inside a code block in the Deployment section. --- samples/http/facebook/README.md | 2 ++ samples/http/forgerock-openam/README.md | 2 ++ samples/http/okta-discovery/README.md | 2 ++ samples/http/onelogin-jit/README.md | 2 ++ samples/http/twitter/README.md | 2 ++ samples/http/wordpress/README.md | 2 ++ samples/ssh/generic-linux-ssh-keys/README.md | 2 ++ samples/ssh/generic-linux-with-ad/README.md | 2 ++ samples/ssh/generic-linux-with-discovery/README.md | 2 ++ samples/ssh/generic-linux/README.md | 2 ++ samples/ssh/linux-app-text-config/README.md | 2 ++ samples/ssh/linux-ssh-batch-mode/README.md | 2 ++ samples/ssh/restricted-authorized-key/README.md | 2 ++ samples/ssh/vcenter-appliance/README.md | 2 ++ samples/telnet/cisco-ios/README.md | 2 ++ samples/telnet/racf-tn3270/README.md | 2 ++ 16 files changed, 32 insertions(+) diff --git a/samples/http/facebook/README.md b/samples/http/facebook/README.md index 4fef2d6..9562322 100644 --- a/samples/http/facebook/README.md +++ b/samples/http/facebook/README.md @@ -4,6 +4,8 @@ This sample validates and changes Facebook account passwords by replaying Facebook's browser-based login and security forms. It is a form-scraping example for sites that do not expose a supported administrative API. +**Platform Script:** [`CustomFacebook.json`](./CustomFacebook.json) + ## Target System Facebook user accounts accessed through the public web interface. diff --git a/samples/http/forgerock-openam/README.md b/samples/http/forgerock-openam/README.md index e9bb577..079c11c 100644 --- a/samples/http/forgerock-openam/README.md +++ b/samples/http/forgerock-openam/README.md @@ -4,6 +4,8 @@ This sample manages ForgeRock AM/OpenAM users through the REST API for a specific realm. It uses header-based authentication to validate credentials and to set a user's `userpassword` attribute. +**Platform Script:** [`Forgerock_OpenAM.json`](./Forgerock_OpenAM.json) + ## Target System ForgeRock AM / OpenAM user accounts in a target realm. diff --git a/samples/http/okta-discovery/README.md b/samples/http/okta-discovery/README.md index 7556c9d..c0df116 100644 --- a/samples/http/okta-discovery/README.md +++ b/samples/http/okta-discovery/README.md @@ -4,6 +4,8 @@ This sample combines password validation, password reset, account discovery, and group-membership restore/suspend workflows for Okta. It uses an Okta API token for administrative operations and the `/authn` endpoint for end-user password checks. +**Platform Script:** [`Okta_WithDiscoveryAndGroupMembershipRestore.json`](./Okta_WithDiscoveryAndGroupMembershipRestore.json) + ## Target System Okta users and group memberships in an Okta tenant. diff --git a/samples/http/onelogin-jit/README.md b/samples/http/onelogin-jit/README.md index 1f193cc..8f989be 100644 --- a/samples/http/onelogin-jit/README.md +++ b/samples/http/onelogin-jit/README.md @@ -4,6 +4,8 @@ This sample is a JIT-focused add-on for OneLogin environments already managed through a separate Generic REST connector. It validates OAuth client credentials, enables or disables users, and elevates or demotes users by assigning or removing OneLogin roles. +**Platform Script:** [`OneLogin_GRC_JIT_addon.json`](./OneLogin_GRC_JIT_addon.json) + ## Target System OneLogin users and role assignments used in Safeguard JIT access workflows. diff --git a/samples/http/twitter/README.md b/samples/http/twitter/README.md index a6a12ef..0e0bde6 100644 --- a/samples/http/twitter/README.md +++ b/samples/http/twitter/README.md @@ -4,6 +4,8 @@ This sample validates and changes Twitter account passwords by walking the site's login and settings forms over HTTP. It also detects several common challenge states that prevent unattended password management. +**Platform Script:** [`CustomTwitter.json`](./CustomTwitter.json) + ## Target System Twitter/X user accounts accessed through the web interface. diff --git a/samples/http/wordpress/README.md b/samples/http/wordpress/README.md index cc5c16f..d17b0a3 100644 --- a/samples/http/wordpress/README.md +++ b/samples/http/wordpress/README.md @@ -4,6 +4,8 @@ This sample manages WordPress users through the REST API using HTTP Basic authentication. It is a straightforward API example for environments where the site exposes authenticated REST endpoints for user administration. +**Platform Script:** [`WordPressHttp.json`](./WordPressHttp.json) + ## Target System WordPress user accounts exposed through the WordPress REST API. diff --git a/samples/ssh/generic-linux-ssh-keys/README.md b/samples/ssh/generic-linux-ssh-keys/README.md index 9783534..66ef515 100644 --- a/samples/ssh/generic-linux-ssh-keys/README.md +++ b/samples/ssh/generic-linux-ssh-keys/README.md @@ -4,6 +4,8 @@ This sample adds SSH authorized-key management to the generic Linux password-management flow. It can discover existing keys, check whether a specific key is installed, add a new key, optionally test the matching private key, and remove an old key. +**Platform Script:** [`GenericLinuxWithSSHKeySupport.json`](./GenericLinuxWithSSHKeySupport.json) + ## Target System A Linux or Unix host that uses OpenSSH-style `AuthorizedKeysFile` paths for managed accounts. diff --git a/samples/ssh/generic-linux-with-ad/README.md b/samples/ssh/generic-linux-with-ad/README.md index 3087b3c..e73a6fb 100644 --- a/samples/ssh/generic-linux-with-ad/README.md +++ b/samples/ssh/generic-linux-with-ad/README.md @@ -4,6 +4,8 @@ This sample is the generic Linux SSH password-management script with an optional Active Directory-style login name for the service account. It appends `@domain` during SSH login, then uses the same local `/etc/passwd` and `/etc/shadow` workflow to validate and change managed-account passwords. +**Platform Script:** [`GenericLinuxWithAD.json`](./GenericLinuxWithAD.json) + ## Target System A Linux host where the service account may authenticate as `user@domain`, while password management still targets local accounts on the host. diff --git a/samples/ssh/generic-linux-with-discovery/README.md b/samples/ssh/generic-linux-with-discovery/README.md index 93c442c..aa83140 100644 --- a/samples/ssh/generic-linux-with-discovery/README.md +++ b/samples/ssh/generic-linux-with-discovery/README.md @@ -4,6 +4,8 @@ This sample extends the generic Linux SSH password-management flow with account discovery. In addition to checking and changing passwords, it enumerates Unix accounts and group memberships and reports them back to Safeguard. +**Platform Script:** [`GenericLinuxWithDiscovery.json`](./GenericLinuxWithDiscovery.json) + ## Target System A generic Linux host with local accounts in `/etc/passwd`, password hashes in `/etc/shadow`, and standard Unix identity commands such as `id` and `awk`. diff --git a/samples/ssh/generic-linux/README.md b/samples/ssh/generic-linux/README.md index dde87ec..8266998 100644 --- a/samples/ssh/generic-linux/README.md +++ b/samples/ssh/generic-linux/README.md @@ -4,6 +4,8 @@ This sample manages local Linux account passwords over SSH using an interactive shell. It verifies service-account access, validates managed-account passwords against `/etc/shadow`, and changes passwords with `passwd`. +**Platform Script:** [`GenericLinux.json`](./GenericLinux.json) + ## Target System A generic Linux host with local accounts in `/etc/passwd` and password hashes in `/etc/shadow`. diff --git a/samples/ssh/linux-app-text-config/README.md b/samples/ssh/linux-app-text-config/README.md index 263f189..a8f660b 100644 --- a/samples/ssh/linux-app-text-config/README.md +++ b/samples/ssh/linux-app-text-config/README.md @@ -4,6 +4,8 @@ This sample changes an application password stored in a plain-text configuration file on Linux over SSH. It is intended for simple legacy applications where password rotation means replacing a single `prefix + password` line in a file. +**Platform Script:** [`LinuxApplicationTextConfig.json`](./LinuxApplicationTextConfig.json) + ## Target System A Linux host that stores an application password in a text file that can be updated with shell tools such as `sed`. diff --git a/samples/ssh/linux-ssh-batch-mode/README.md b/samples/ssh/linux-ssh-batch-mode/README.md index c25051b..6a12f86 100644 --- a/samples/ssh/linux-ssh-batch-mode/README.md +++ b/samples/ssh/linux-ssh-batch-mode/README.md @@ -4,6 +4,8 @@ This sample shows how to manage Linux passwords without an interactive shell by using `ExecuteCommand`. It is a good starting point for targets where non-interactive command execution is more reliable than prompt-driven `Send` and `Receive` flows. +**Platform Script:** [`LinuxSshBatchModeExample.json`](./LinuxSshBatchModeExample.json) + ## Target System A Linux host where the service account can run the required commands through non-interactive SSH. diff --git a/samples/ssh/restricted-authorized-key/README.md b/samples/ssh/restricted-authorized-key/README.md index cd65901..00378b4 100644 --- a/samples/ssh/restricted-authorized-key/README.md +++ b/samples/ssh/restricted-authorized-key/README.md @@ -4,6 +4,8 @@ This sample demonstrates a least-privilege SSH design where the service account authenticates only with a restricted authorized key. It uses non-interactive command execution plus passwordless `sudo` to validate and rotate local Linux account passwords. +**Platform Script:** [`RestrictedAuthorizedKeyExample.json`](./RestrictedAuthorizedKeyExample.json) + ## Target System A Linux host where the Safeguard service account uses a restricted SSH key and passwordless `sudo` for a tightly limited command set. diff --git a/samples/ssh/vcenter-appliance/README.md b/samples/ssh/vcenter-appliance/README.md index f50b80f..1d5798a 100644 --- a/samples/ssh/vcenter-appliance/README.md +++ b/samples/ssh/vcenter-appliance/README.md @@ -4,6 +4,8 @@ This sample manages privileged accounts on VMware vCenter Server Appliance over SSH. It can validate the appliance configuration, rotate vCenter SSO administrator passwords, keep `root` synchronized when required, and discover members of the `Administrators` group. +**Platform Script:** [`vCenterServerAppliance.json`](./vCenterServerAppliance.json) + ## Target System A VMware vCenter Server Appliance (VCSA) with SSH and shell access enabled. diff --git a/samples/telnet/cisco-ios/README.md b/samples/telnet/cisco-ios/README.md index d7ab7a8..fc92ad7 100644 --- a/samples/telnet/cisco-ios/README.md +++ b/samples/telnet/cisco-ios/README.md @@ -4,6 +4,8 @@ This sample manages local Cisco IOS credentials over Telnet. It can validate accounts, change local user passwords, and rotate the device's enable password while preserving the existing password style and privilege context. +**Platform Script:** [`GenericCiscoIosTelnet.json`](./GenericCiscoIosTelnet.json) + ## Target System Cisco IOS network devices that expose Telnet and local `username` / `enable` configuration entries. diff --git a/samples/telnet/racf-tn3270/README.md b/samples/telnet/racf-tn3270/README.md index 2287cf9..13f7cf8 100644 --- a/samples/telnet/racf-tn3270/README.md +++ b/samples/telnet/racf-tn3270/README.md @@ -4,6 +4,8 @@ This sample manages RACF passwords through a TN3270 session to a z/OS logon screen. It validates logon for both service and managed accounts and changes passwords by issuing a RACF `ALU ... PASSWORD(...) NOEXPIRED` command. +**Platform Script:** [`GenericRacfTn3270.json`](./GenericRacfTn3270.json) + ## Target System IBM z/OS RACF user IDs exposed through a TN3270 / TSO logon session.