# PAM Project KCM Import

`pam project kcm-import` reads connections, users and groups directly from a Keeper Connection Manager (a.k.a. Apache Guacamole) database, maps the 150+ KCM connection parameters to KeeperPAM equivalents via `kcm_mappings.json`, then delegates to `pam project import` (new project) or `pam project extend` (existing project) under the hood. You never write the import JSON by hand.

### Synopsis

```
pam project kcm-import
  ( --db-host=HOST [--db-port=PORT] [--db-name=DB] [--db-user=USER]
    [--db-password-record=UID] [--db-ssl]
  | --docker-detect [--docker-container=NAME] )
  [--db-type=mysql|postgresql]
  [--name=PROJECT | --config=UID_OR_TITLE]
  [--gateway=UID_OR_NAME]
  [--folder-mode=ksm|exact|flat]
  [--output=FILE | --dry-run]
  [--skip-users]
  [--include-disabled]
```

Either `--db-host` **or** `--docker-detect` is required. Everything else has a default.

### Prerequisites

* Authenticated Commander session — run `keeper login` first.
* Network access to the KCM database from wherever you're running Commander.
* Database driver installed in the same Python that runs Commander:
  * **MySQL** → `pymysql` (preferred) or `mysql-connector-python`. Install: `pip3 install pymysql`
  * **PostgreSQL** → `psycopg2-binary`. Install: `pip3 install psycopg2-binary`
* `docker` CLI on `PATH` if using `--docker-detect`.
* A Keeper role with permission to create PAM projects, gateways and shared folders.
* `kcm_mappings.json` present at `keepercommander/commands/pam_import/kcm_mappings.json`.

### Command Line Options

Either `--db-host` or `--docker-detect` is required; everything else has a default.

#### Source: where to read KCM from

* `--db-host` → KCM database hostname or IP. **Required unless `--docker-detect` is used.**
* `--docker-detect` → Auto-detect host/port/db/user/password by reading `MYSQL_*` / `POSTGRES_*` env vars from a Docker container via `docker inspect`. **Required unless `--db-host` is used.**
* `--docker-container` → Container name to inspect when `--docker-detect` is set. Default: `guacamole`.
* `--db-type` → Database engine. Choices: `mysql`, `postgresql`. Default: `mysql`.
* `--db-port` → TCP port for the DB. Default: `3306` for mysql, `5432` for postgresql.
* `--db-name` → Database name. Default: `guacamole_db`.
* `--db-user` → DB username. Default: `guacamole_user`. Read-only `SELECT` is enough.
* `--db-password-record` → Keeper record UID containing the DB password. Without this and without `--docker-detect`, the command prompts for the password via hidden `getpass`. Never accepts a password as a CLI argument.
* `--db-ssl` → Require TLS for the DB connection. The command emits a warning if you connect to a non-local/non-RFC1918 host without it.

#### Destination: where the data lands in Keeper

* `--name`, `-n` → Project name for a brand-new import. Default: `KCM-Import-<YYYYMMDD-HHMMSS>`. **Mutually exclusive with `--config`.**
* `--config`, `-c` → Existing PAM Configuration UID or title. Switches to **extend mode**. **Mutually exclusive with `--name`.**
* `--gateway`, `-g` → Existing gateway UID or name. Without it an interactive picker lists online gateways. Ignored in `--config` mode.
* `--folder-mode` → How KCM connection groups map to Keeper shared folders. Choices: `ksm`, `exact`, `flat`. Default: `ksm`.

#### Output mode

These three are mutually exclusive. Default (no flag) imports into the vault.

* `--dry-run`, `-d` → Print the assembled `pam_data` JSON to stdout with sensitive fields replaced by `[REDACTED]`. No vault writes.
* `--output`, `-o` → Write the full JSON (cleartext passwords) to a `0600` file. No vault writes.
* *(no flag)* → Default. Delegates to `pam project import` (new project) or `pam project extend` (existing config).

#### Filtering

* `--skip-users` → Skip generating `pamUser` records.
* `--include-disabled` → Include KCM connections marked disabled (`max_connections == 0`).

### Quick Start

```bash
# KCM running locally in Docker, auto-detect credentials:
pam project kcm-import --docker-detect --docker-container=guacamole \
    --db-type=postgresql --name="My KCM Migration" --dry-run

# Remote KCM database, prompt for password:
pam project kcm-import --db-host=kcm-db.internal --db-type=postgresql \
    --db-name=guacamole_db --db-user=guacamole_user \
    --name="Prod KCM" --dry-run

# Pull the password from a Keeper record instead of prompting:
pam project kcm-import --db-host=kcm-db.internal --db-type=postgresql \
    --db-password-record=<RECORD_UID> --name="Prod KCM" --dry-run
```

> Always run with `--dry-run` first, then drop it to actually create the project.

### Three Ways to Give It Database Credentials

* **Docker auto-detect** (`--docker-detect [--docker-container=NAME]`) → KCM running in Docker on the same host as Commander.
* **Vault record** (`--db-host=H --db-password-record=<UID>`) → Repeatable runs / scripted migrations.
* **Interactive prompt** (`--db-host=H` with no record) → One-off run. Hidden `getpass` prompt. Never pass the password as a CLI argument.

#### Database Privileges Needed

The DB user needs **`SELECT`** on these Guacamole tables (read-only; the command never writes to the source DB):

* `guacamole_connection`
* `guacamole_connection_parameter`
* `guacamole_connection_attribute`
* `guacamole_connection_group`
* `guacamole_connection_permission`
* `guacamole_entity`
* `information_schema.tables`

**PostgreSQL — minimal read-only role:**

```sql
CREATE USER keeper_kcm_ro WITH PASSWORD '...';
GRANT CONNECT ON DATABASE guacamole_db TO keeper_kcm_ro;
GRANT USAGE ON SCHEMA public TO keeper_kcm_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO keeper_kcm_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO keeper_kcm_ro;
```

**MySQL — minimal read-only role:**

```sql
CREATE USER 'keeper_kcm_ro'@'%' IDENTIFIED BY '...';
GRANT SELECT ON guacamole_db.* TO 'keeper_kcm_ro'@'%';
FLUSH PRIVILEGES;
```

### Folder Layout — `--folder-mode`

KCM organises connections in a tree of "connection groups". This flag controls how that tree maps to Keeper shared folders. Resources land under `KCM Resources - <root>` and users under `KCM Users - <root>` regardless of mode.

* `ksm` *(default)* → Groups marked with a KSM config become roots; descendants stay nested under them.
* `exact` → Mirrors the full KCM group hierarchy 1:1.
* `flat` → Every group becomes a top-level shared folder (no nesting).

> Group names are sanitised — `/`, `\` and `..` are replaced with `_` to prevent path traversal.

### Protocol → Record-Type Mapping

| KCM protocol    | Keeper record type | Notes                                            |
| --------------- | ------------------ | ------------------------------------------------ |
| `ssh`           | `pamMachine`       | SFTP sub-resources auto-extracted                |
| `rdp`           | `pamMachine`       | RDP options mapped via `kcm_mappings.json`       |
| `vnc`           | `pamMachine`       |                                                  |
| `telnet`        | `pamMachine`       | Login regex parameters carry over                |
| `kubernetes`    | `pamMachine`       | Namespace/pod/container/cert fields carry over   |
| `mysql`         | `pamDatabase`      | `database_type = "mysql"`                        |
| `postgres`      | `pamDatabase`      | KCM uses `postgres`, KeeperPAM uses `postgresql` |
| `sql-server`    | `pamDatabase`      |                                                  |
| `http`          | `pamRemoteBrowser` | RBI only                                         |
| *anything else* | `pamMachine`       | Unknown protocols fall back                      |

Each non-disabled KCM connection produces one resource record, one `pamUser` record wired as `launch_credentials`, and (SSH with SFTP) an extra SFTP resource + user pair.

### What Gets Created in the Vault

Running without `--dry-run` / `--output` produces (in order):

1. **Top-level project folder** named after `--name`.
2. **Two shared folders per group root** — `KCM Resources - <root>` and `KCM Users - <root>`.
3. **A PAM Configuration record** (skipped in extend mode).
4. **A gateway** — reused if `--gateway` is given, otherwise interactive picker with `[N] Create new`.
5. **Resource records** (`pamMachine` / `pamDatabase` / `pamRemoteBrowser`).
6. **User records** (`pamUser`) wired to their resource's `launch_credentials`.
7. **SFTP sub-resources and sub-users** for SSH connections with SFTP configured.

### Gateway Selection

* `--gateway=<UID|name>` → reuse that existing gateway (must be online).
* No `--gateway` flag → interactive prompt; pick a number to reuse or `N` to provision a new one.
* No online gateways available → falls through to "create new" without prompting.
* `--config` mode → gateway flag is ignored.

### Output Mode Details

```bash
# 1. Dry-run: print JSON to stdout with passwords [REDACTED]
pam project kcm-import --db-host=... --dry-run

# 2. Output file: write full JSON (cleartext passwords) to a 0600 file
pam project kcm-import --db-host=... --output=/tmp/kcm-export.json

# 3. Default: import into the vault
pam project kcm-import --db-host=... --name="Prod KCM"
```

### Re-Running and Cleanup

**Re-running with the same `--name`** — `pam project import` matches records by title and skips existing ones. New connections are added; changes to existing connections are not picked up automatically.

**To add new KCM connections to an existing project** — use `--config=<UID_OR_TITLE>` instead of `--name`.

**Rolling back an import:**

1. Right-click the project folder in the vault → **Delete folder**.
2. Delete the gateway separately via Admin Console → Privileged Access → Gateways (if needed).

> **Best practice:** always do `--dry-run` first, then `--output=/tmp/kcm-preview.json` to inspect with cleartext values, then the real import.

### Examples

```bash
# 1. Smoke test — local Docker, PostgreSQL, 3 connections
pam project kcm-import --db-host=192.168.32.10 --db-type=postgresql \
    --db-user=guacamole_user --db-name=guacamole_db --db-port=5432 \
    --name="Live-Test-Guac" --dry-run

# 2. Larger POC — 25 HTTP+SSH connections
pam project kcm-import --db-host=172.29.0.3 --db-type=postgresql \
    --db-user=guacamole_user --db-name=guacamole_db --db-port=5432 \
    --name="Live-Test-KCM" --dry-run

# 3. Save full JSON to disk for review
pam project kcm-import --db-host=192.168.32.10 --db-type=postgresql \
    --db-user=guacamole_user --db-name=guacamole_db --db-port=5432 \
    --name="Output-Test" --output=/tmp/kcm-output-test.json

# 4. Skip user records
pam project kcm-import --db-host=172.29.0.3 --db-type=postgresql \
    --db-user=guacamole_user --db-name=guacamole_db --db-port=5432 \
    --name="Skip-Users-Test" --skip-users --dry-run

# 5. Flat folder mode
pam project kcm-import --db-host=172.29.0.3 --db-type=postgresql \
    --db-user=guacamole_user --db-name=guacamole_db --db-port=5432 \
    --name="Flat-Mode-Test" --folder-mode=flat --dry-run

# 6. Docker auto-detect
pam project kcm-import --docker-detect --docker-container=kcm-setup-guacamole-1 \
    --db-type=postgresql --name="Docker-Detect-Test" --dry-run
```

### Common Pitfalls

* **`Either --db-host or --docker-detect is required`** → you forgot both; one is mandatory.
* **`No PAM configuration found for gateway "X"`** → the gateway doesn't have a PAM config yet. Run `pam config create` first.
* **`Database connection failed: <ErrorType>`** → Commander hides the underlying message to avoid leaking credentials. Re-run with debug logging (`-d` at shell-launch) to see the full exception.
* **Connecting to a remote DB without `--db-ssl`** → you'll get a `WARNING`; credentials transit in cleartext. Always set `--db-ssl` for non-local hosts.
* **Docker auto-detect with the wrong container name** → default is `guacamole`; Compose deployments often use `<project>-guacamole-1`. Run `docker ps --format '{{.Names}}'` to check.
* **`pam project import` complains about `kcm_mappings.json` missing** → the file ships with Commander. Verify with `git ls-files keepercommander/commands/pam_import/kcm_mappings.json`; if missing, fetch from upstream `release`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/keeperpam/commander-cli/command-reference/keeperpam-commands/pam-project-kcm-import.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
