Rds Db2
AWSOfficialPublished by AWS
- Skill
- Tool integration
- Cloud and DevOps
- Apache-2.0
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured framework for the lifecycle management of Amazon RDS for Db2. It bridges the gap between general IBM Db2 knowledge and the specific constraints of the AWS managed service, such as the absence of SSH access, the use of rdsadmin procedures, and integration with AWS-native services like Secrets Manager, KMS, and S3. The skill includes detailed workflows for provisioning, cross-region disaster recovery, Kerberos authentication, and complex migrations from on-premises or mainframe environments.
What it does
- Provides AWS CLI-based workflows for provisioning and managing RDS for Db2 instances.
- Details procedures for migrating data from self-managed Db2 LUW or z/OS mainframe environments.
- Configures security controls including customer-managed KMS keys (BYOK) and self-managed Active Directory with Kerberos.
- Manages high availability and disaster recovery via Multi-AZ and cross-region mounted replicas.
- Offers troubleshooting steps for common RDS-specific errors like SQL30082N and S3 integration issues.
- Enforces standardized resource tagging for all created infrastructure.
When to use it
- When deploying or managing Amazon RDS for Db2 instances in production environments.
- When migrating existing IBM Db2 databases from on-premises Linux, AIX, Windows, or z/OS systems.
- When implementing enterprise security requirements like BYOK encryption or Kerberos authentication.
- When configuring cross-region disaster recovery or high-availability standby replicas.
- When troubleshooting connectivity or authentication issues specific to the RDS-managed environment.
Advantages
- Provides RDS-specific operational constraints that general Db2 documentation often omits.
- Standardizes infrastructure deployment with consistent tagging and security best practices.
- Reduces manual effort for complex tasks like S3-based backup/restore and AD domain joining.
- Ensures compliance with AWS-native security patterns like confused-deputy protection.
- Offers clear, actionable CLI commands for deterministic operations.
Usage examples
- “How do I fix SQL30082N errors on my RDS for Db2 instance?”
- The agent identifies that the master password likely rotated in Secrets Manager and instructs the user to run the db2_use helper to refresh the local credential cache.
- “Create a cross-region standby replica for my Db2 instance in us-west-2.”
- The agent provides the exact AWS CLI command using --replica-mode mounted and lists the six mandatory prerequisites, including automated backups and custom parameter groups.
- “How can I use my own KMS key to encrypt my RDS for Db2 instance?”
- The agent explains the BYOK workflow, including the requirement for a multi-region key, the need for specific IAM permissions, and the process for importing key material.
- “Can I use unfenced C stored procedures on RDS for Db2?”
- The agent states clearly that unfenced C/COBOL procedures are not supported and provides guidance on rewriting them in Java or SQL PL.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/aws/rds-db2/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip rds-db2.zip -d .agents/skills
Documentation by AWS
Amazon RDS for Db2
Overview
Amazon RDS for Db2 is a managed IBM Db2 LUW service. RDS for Db2 is managed — you cannot SSH to the host, install agents, or run unfenced external stored procedures in C/COBOL. Java stored procedures work via sqlj.install_jar. This skill covers the operator lifecycle: provisioning with IBM licensing, client install and TLS connectivity, migration from self-managed Db2 on Linux/AIX/Windows/z/OS/AS400, S3 backup and restore, Multi-AZ and cross-region standby replicas, and RDSADMIN stored procedures that replace SYSCTRL/SYSMAINT authority.
It also covers six additional security and operations areas: customer-managed KMS keys (BYOK), self-managed Active Directory with Kerberos authentication, Db2 audit to S3, code page and collation selection (EBCDIC, CCSID), minimum IAM permissions, and EC2/RDS colocation for Multi-AZ latency and failover.
The AWS MCP server is recommended but not required; all operations are expressed in AWS CLI syntax and run with or without it.
Routes to the matching sub-skill reference. Load only the matching reference.
Common Tasks
Verify Dependencies
Before executing an RDS for Db2 workflow, confirm required tools exist. Do not run installers or API calls yet.
AWS CLI v2 for every RDS API call
AWS credentials via managed mechanism (IAM role, instance profile,
ada credentials update) — not pasted credentialsClient install:
bash/curlaccess, run as root and asdb2inst1Airgap install: internet-connected machine + target with VPC endpoints for S3, SSM, Secrets Manager
Mainframe migration: z/OS access, IBM ADB2GEN license, Python 3
BYOK / customer-managed KMS:
openssl(to wrap imported key material) andjq(to parseget-parameters-for-importoutput)Self-managed Active Directory + Kerberos:
realmd,sssd,adcli,krb5-workstationon the client, plus a valid Kerberos ticket (kinitproduces a TGT — check withklist)Kerberos JDBC test: a JDK and the Db2 JDBC driver
db2jcc4.jarv4.33+ (earlier driver versions lacksecurityMechanism=11Kerberos support)
Constraints:
The agent MUST check dependencies before running any install or AWS API command.
The agent MUST NOT prompt the user to paste credentials because credentials MUST flow through an IAM role or instance profile.
The agent MUST tell the user which dependencies are missing and MUST respect the user's decision to abort.
The agent MUST explain, for each step, what it does, why, and which tool will be invoked — before invoking it.
Classify and Route
Map the user's question to the correct sub-skill reference, then load only that file.
| User says | Sub-skill | Load |
|---|---|---|
| create / provision / parameter group / IBM customer ID / IBM site ID / License Manager / BYOL / GovCloud | provisioning | provisioning.md |
| connect / SQL30082N / SQL1531N / DSN / CLP / Python / Java / CloudShell / airgap | connectivity | connectivity.md |
| SSL / TLS / GSKit / certificate / truststore / bundle.pem | connectivity-tls | connectivity-tls.md |
| Python driver / JDBC / laptop / multi-instance / db2_use | connection drivers | connection-drivers.md |
| migrate / DMS / Q Replication / IIDR / AIX / Windows / AS400 / precheck | migration | migration.md |
| z/OS / mainframe / ADB2GEN / schema conversion | mainframe-migration | mainframe-migration.md |
| code page / collation / CCSID / EBCDIC / UTF-8 / CODEUNITS32 / territory | code page & collation | code-page-collation.md |
| snapshot / backup / restore / rollforward / PiTR / S3 integration | backup-restore | backup-restore.md |
| Multi-AZ / standby replica / read replica / HADR / cross-region / failover | ha-dr | ha-dr.md |
| parameter group / RDSADMIN / scale / storage / CloudWatch / registry variable | operations | operations.md |
| BYOK / customer-managed KMS / bring your own key / imported key material / multi-region key | byok | byok-kms.md |
| Active Directory / Kerberos / domain join / self-managed AD / kinit / SPN / realm | ad-kerberos | ad-kerberos.md |
| audit / DB2_AUDIT / audit policy / audit to S3 / option group | db2-audit | db2-audit.md |
| minimum IAM / least privilege / IAM policy / trust policy / permissions | minimum-iam | minimum-iam.md |
| colocation / co-locate / EC2 app latency / ASG / ALB / failover routing | colocation | colocation.md |
Constraints:
The agent MUST read only the reference files that match the user's question, to keep the context focused.
The agent MUST NOT invent RDSADMIN procedure signatures, because wrong parameter order will fail at runtime — always cite the signature from the reference file.
The agent MUST cite the source blog URL when an answer is blog-sourced, so the user can verify specifics.
If a question crosses two sub-skills (e.g. "migrate z/OS with near-zero downtime", or "BYOK plus cross-region standby"), the agent SHOULD load each matching reference and combine them.
Execute Workflow
Once routed, give the user a concrete, runnable answer grounded in the reference file.
Parameter acquisition:
All required parameters (region, instance identifier, source/target ARNs, S3 bucket, prefix, the
--master-usernamevalue) MUST be collected upfront in a single message.Parameter formats MUST be specified: region
us-east-1-style; instance identifier^[a-zA-Z][a-zA-Z0-9-]{0,62}$; ARNarn:aws:rds:<region>:<account>:db:<name>; S3 bucket 3–63 chars lowercase.The agent MUST accept parameters via direct input, a JSON/YAML file path, or a URL.
Tool use:
Use AWS CLI for RDS operations (example:
aws rds create-db-instance-read-replica --db-instance-identifier <name> --source-db-instance-identifier <arn> --replica-mode mounted --region <dr-region>). Every operation is expressed in AWS CLI syntax so it runs whether or not the AWS MCP server is installed.Use bundled scripts — db2-driver.sh, db2client-configure.sh, db2client-airgap.sh, functions.sh — instead of rewriting install steps.
Write migration plans, upgrade plans, validation reports to a local
artifacts/<app-name>/directory created at runtime in the working directory (this is a run-time output location, not part of the shipped skill).
Constraints:
The agent MUST give exact CLI commands when behavior is deterministic, not descriptions like "enable Multi-AZ".
The agent MUST obtain AWS credentials through an IAM role or instance profile and MUST NOT prompt the user to paste credentials.
The agent MUST cite the source blog or documentation URL whenever the answer is sourced from published material, so the user can verify it.
The agent MUST write long-form artifacts to a local
artifacts/<app-name>/directory (created at runtime in the working directory) so the workspace is inspectable.When a requested action is unsupported on managed RDS for Db2, the agent MUST state the limitation plainly — no hedging like "limited support" or "with special config" — and offer a supported alternative (for example, unfenced C/COBOL stored procedures are not supported; rewrite them in Java via
sqlj.install_jar, or in SQL PL).The AWS MCP server is recommended but not required. When the MCP server is in use, prefer inline JSON strings over positional filesystem arguments with
call_aws; this is guidance for the MCP path only, not a requirement. Never treatcall_awsorrun_scriptas the only way to perform an operation — the AWS CLI command always works on its own.
Resource tagging (always apply on resource creation)
When creating any RDS for Db2 resource (create-db-instance, create-db-parameter-group, create-db-cluster for Multi-AZ deployments, create-db-instance-read-replica for standby replicas), ALWAYS include these tags using the RDS list-of-records --tags syntax:
--tags Key=created_by,Value=rds-db2-skill Key=generation_model,Value={your-model-id}
Example: --tags Key=created_by,Value=rds-db2-skill Key=generation_model,Value=claude-sonnet-4-20250514
The same convention applies to every resource this skill creates, including BYOK-encrypted instances (create-db-instance --storage-encrypted --kms-key-id ...), audit option groups (create-option-group), and customer-managed KMS keys (kms create-key, tagged with --tags TagKey=created_by,TagValue=rds-db2-skill TagKey=generation_model,TagValue={your-model-id} per the KMS tag syntax).
Include these tags even if the user does not mention tagging, so that they can identify the resources created via this skill. If the user provides additional tags, append these to their tags rather than replacing them. The same applies when tagging via add-tags-to-resource on existing resources.
RDS-managed facts the agent must always surface
These RDS-for-Db2-specific facts are what differentiates this skill from general IBM Db2 knowledge. General-Db2 answers typically omit the RDS-managed constraints (no unfenced C/COBOL, Secrets Manager rotation side effects, rdsadmin.* procedures) and the AWS-native migration tooling nuances (DMS z/OS limits, ADB2GEN vs SCT).
For "create cross-region standby replica for DR", you MUST tell the user ALL of the following six facts:
Use
aws rds create-db-instance-read-replicawith--replica-mode mountedand the cross-region source ARN — Db2 cross-region standby uses mounted replica mode, NOT transactional read-replica mode.Source prerequisite: automated backups enabled on the source instance (backup retention period > 0).
Target-region prerequisite: custom parameter group created in the target region before the command runs.
Target-region prerequisite: KMS key available in the target region (multi-region KMS key or a target-region customer-managed KMS key).
State prerequisites: all databases in
activestate, no pending reboots, no license-model restrictions blocking cross-region replicas.Explain the mounted-vs-transactional distinction — mounted replicas do not accept reads or SQL from applications; they exist purely as a DR standby that can be promoted. Do not suggest read offload use cases.
For "restore Db2 backup from S3 (multi-part, N files)", you MUST tell the user ALL of the following six facts — never omit any of the procedure names:
Attach IAM role with S3 access via
aws rds add-role-to-db-instanceusing--feature-name S3_INTEGRATION.Set restore performance parameters via
rdsadmin.set_configuration— tuneUSE_STREAMING_RESTORE,RESTORE_DATABASE_NUM_BUFFERS, andPARALLELISMbefore starting the restore.Call
rdsadmin.restore_databasewith five parameters in this exact order: database name, restore mode (OFFLINEorONLINE), S3 prefix, S3 bucket, and region. Multi-file (multi-part) backups are handled by the shared prefix — there is no separate multi-part flag parameter. (Signature:rdsadmin.restore_database(dbname, type, prefix, bucket, region).)For
ONLINErestore mode, follow up withrdsadmin.rollforward_databaseto replay archive logs, thenrdsadmin.complete_rollforwardto finish.OFFLINErestores do NOT need rollforward.Monitor progress with
rdsadmin.get_task_status— everyrdsadminprocedure returns a task ID you poll.Warn about VPC endpoint for S3 if no internet egress from the private subnet, and warn about Db2 version compatibility between the source backup and the RDS instance engine version (forward-compatible, not backward).
For "C/COBOL unfenced external stored procedures — lift and shift to RDS for Db2?", you MUST tell the user ALL of the following four facts:
Unfenced external stored procedures in C and COBOL are NOT supported on RDS for Db2. State this as an unqualified "not supported" — do not hedge with "limited support" or "with special config."
All routines on RDS for Db2 MUST be fenced. This is a managed-service architectural constraint, not a flag.
Java stored procedures are supported — install via
sqlj.install_jar. C/COBOL SPs should be rewritten in Java or SQL PL (Db2's procedural SQL, equivalent to Oracle's PL/SQL).Offer to help identify which SPs are unfenced and prioritize the rewrite by call frequency (hot code path first).
For "migrate Db2 for z/OS to RDS for Db2 with near-zero downtime", you MUST tell the user ALL of the following five facts:
For near-zero-downtime from z/OS, use Q Replication (IBM IIDR), Qlik Replicate, or Precisely — these are the CDC tools that support Db2 for z/OS as a source streaming to RDS for Db2.
AWS DMS supports FULL LOAD ONLY from Db2 for z/OS. DMS does NOT support CDC from z/OS sources. Use DMS for a one-time bulk load, not for near-zero-downtime cutover.
Use ADB2GEN for schema conversion from z/OS. AWS SCT does NOT support Db2 for z/OS as a source — this is a common trap. Do not recommend SCT for z/OS sources.
Code-page conversion (EBCDIC → UTF-8) is the primary migration risk. Plan explicit collation and code page mapping before cutover — silent data corruption is the failure mode.
Plan explicit collation selection on the target RDS instance to match the semantic ordering of the z/OS source.
For "SQL30082N — USERNAME AND/OR PASSWORD INVALID" with RDS-managed master user (user didn't change it), you MUST tell the user ALL of the following four facts:
SQL30082N after a previously-working connection almost always means the master password rotated in Secrets Manager. RDS for Db2 rotates the master password on the Secrets Manager schedule — clients using a cached password will start failing with SQL30082N even though nothing on their side changed.
Fix: run
db2_use <instance-id>(fromfunctions.sh/ the bundled helpers). This fetches the current password from Secrets Manager and rewrites~/.db2envwith the new value.Alternative:
db2_test_connectionto verify the helper's fix worked end-to-end.If
db2_useisn't installed, the user needs to pull the current password withaws secretsmanager get-secret-valueand update their local credential cache manually. Do not tell them to rotate the password — the password rotation is what caused the problem.
For "BYOK / customer-managed KMS key for RDS for Db2", you MUST tell the user ALL of the following six facts:
Use a multi-region KMS key with
--origin EXTERNALwhen importing your own key material, so the same key ID and material can replicate to a DR region.The creating principal needs
kms:CreateGrantandkms:DescribeKeyon the key, or instance creation fails.Encryption is set at instance creation with
--storage-encrypted --kms-key-id <alias|arn>. You cannot encrypt an existing unencrypted instance in place — go snapshot →copy-db-snapshot --kms-key-id→restore-db-instance-from-db-snapshot.For cross-region DR, replicate the multi-region key (
kms:ReplicateKey) into the DR region first, thencopy-db-snapshotacross regions with the replica key.Import tokens expire after 24 hours — if
import-key-materialfails on expiry, re-runget-parameters-for-importto get a fresh token and wrapping key.Cite blog DBBLOG-5188 and byok-kms.md; do not invent KMS parameter names.
For "self-managed Active Directory with Kerberos on RDS for Db2", you MUST tell the user ALL of the following six facts:
RDS joins your AD via
--domain-fqdn,--domain-ou,--domain-auth-secret-arn, and--domain-dns-ips— the self-managed AD path, with no AWS Managed Microsoft AD required.The Secrets Manager secret uses keys
SELF_MANAGED_ACTIVE_DIRECTORY_USERNAME(the sAMAccountName only — noDOMAIN\prefix, which fails creation) andSELF_MANAGED_ACTIVE_DIRECTORY_PASSWORD, encrypted by a dedicated KMS key, with a resource policy trustingrds.amazonaws.comguarded byaws:SourceArnandaws:SourceAccount(confused-deputy protection).Delegate the nine AD permissions to a dedicated service account scoped to one OU; grant
servicePrincipalNameread/write on User objects using ADSI Edit, not the ADUC delegation wizard (which filters that attribute out) — this is the most common failure.Open AD ports between RDS and the domain controllers: DNS 53, Kerberos 88 and 464, LDAP 389 and 3268, and the RPC range 49152–65535. Missing the RPC range is the top cause of intermittent join failures. Keep clock skew under 5 minutes.
The RDS master user is a local account that cannot get a Kerberos ticket. AD users need
kinitplus aGRANT CONNECT. Kerberos JDBC usessecurityMechanism=11and a region-specific PEM viasslCertLocation(neverglobal-bundle.pem).Cite the self-managed AD blog and ad-kerberos.md; verify with
describe-db-instances ... DomainMembershipsshowingStatus: joined.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
SQL30082N | Password rotated in Secrets Manager | Run db2_use <instance-id> — the helper re-fetches the current password and rewrites ~/.db2env. |
SQL1531N | DSN not yet in cache | db2 terminate to clear, then retry; if still failing, re-run db2client-configure.sh. |
SQL01013N / TCP timeout | Security group blocks 50000/50443 | Check SG inbound rules — add client's SG on TCP 50000 (plain) or 50443 (SSL). |
| GSKit / SSL certificate error | RDS cert bundle missing or RSA cert not first | Re-download <region>-bundle.pem from RDS truststore and re-run db2client-configure.sh. |
| Access denied on RDSADMIN call | IAM role missing S3_INTEGRATION feature | aws rds add-role-to-db-instance --feature-name S3_INTEGRATION --role-arn <role-arn> --db-instance-identifier <id>. |
| Restore fails with "database in rollforward pending" | ONLINE restore left DB in rollforward state | Call rdsadmin.rollforward_database for each archive log, then rdsadmin.complete_rollforward. |
| Standby replica creation fails | Missing prereqs | Verify: automated backups enabled, custom parameter group in DR region, KMS multi-region key, no pending rdsadmin tasks, all databases in active state. |
AD domain join Status not joined | RPC port range blocked, wrong secret format, or SPN attribute missing | Open RPC 49152–65535 between RDS and the DCs, confirm the secret username is the sAMAccountName with no DOMAIN\ prefix, and grant servicePrincipalName on User objects via ADSI Edit — see ad-kerberos.md. |
import-key-material fails — token expired | Import token older than 24 hours | Re-run get-parameters-for-import for a fresh token and wrapping key, re-wrap the material with openssl, then retry the import — see byok-kms.md. |
| Throttling from RDS API | Exceeded API request rate | Exponential backoff with jitter; batch operations; check Service Quotas. |
Security Considerations
Surface these controls when advising on any production RDS for Db2 deployment. They consolidate the security guidance that the sub-skill references cover in depth.
Encryption at rest and in transit — Always create instances with
--storage-encrypted; for key ownership and compliance use a customer-managed KMS key (BYOK), and for DR use a multi-region key. Enforce TLS on every connection (port 50443,<region>-bundle.pem); never connect in plaintext for production. See byok-kms.md and connectivity-tls.md. (Guideline 1)Least-privilege IAM — Use the scoped policy and trust policy in minimum-iam.md; never attach a
*FullAccessmanaged policy. Scopeiam:PassRoleand ARN-pattern every mutating statement that supports resource-level permissions. (Guideline 5)Network isolation — Keep instances in private subnets, restrict security groups to the application/source SG (never
0.0.0.0/0), and use VPC endpoints for S3/SSM/Secrets Manager so traffic stays off the public internet. See colocation.md. (Guideline 5)Audit logging and monitoring — Enable Db2 audit to S3 (db2-audit.md), RDS Enhanced Monitoring, and CloudTrail for RDS/KMS/Secrets Manager API calls. Alarm on failed logins and configuration changes. (Guideline 12)
Secret rotation — Provision with
--manage-master-user-passwordso RDS stores and rotates the master password in Secrets Manager; never embed plaintext passwords. After rotation, refresh clients withdb2_use <instance-id>. (Guideline 13)Backup encryption and retention — Set a backup retention period, encrypt automated and manual snapshots with your KMS key, and apply S3 bucket encryption plus lifecycle/retention to any Db2 audit or backup buckets. (Guideline 13)
Additional Resources
In-scope documentation and blogs
AWS docs — RDS for Db2: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_RDSDb2.html
AWS docs — RDS for Db2 IAM permissions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAM.html
AWS docs — Kerberos authentication for RDS for Db2: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/db2-kerberos.html
Blog — Connect to RDS for Db2 from CloudShell: https://aws.amazon.com/blogs/database/connect-to-amazon-rds-for-db2-using-aws-cloudshell/
Blog — Restore self-managed Db2 Linux into RDS for Db2: https://aws.amazon.com/blogs/database/restore-self-managed-db2-linux-databases-in-amazon-rds-for-db2/
Blog — Near-zero downtime from AIX/Windows to RDS for Db2 with Q Replication: https://aws.amazon.com/blogs/database/near-zero-downtime-migrations-from-self-managed-db2-on-aix-or-windows-to-amazon-rds-for-db2-using-ibm-q-replication/
Blog — Cross-region standby replicas: https://aws.amazon.com/blogs/database/configure-amazon-rds-for-db2-standby-replicas-for-high-availability-and-faster-disaster-recovery/
Blog — Mainframe DDL conversion (z/OS to RDS for Db2): https://aws.amazon.com/blogs/database/migrating-tables-from-ibm-db2-for-z-os-to-amazon-rds-for-db2/
Blog — Code page and collation for mainframe migration: https://aws.amazon.com/blogs/database/choosing-the-right-code-page-and-collation-for-migration-from-mainframe-db2-to-amazon-rds-for-db2/
Blog — Bring your own customer-managed KMS key for RDS for Db2 (DBBLOG-5188): https://aws.amazon.com/blogs/database/bring-your-own-key-to-amazon-rds-for-db2-with-a-customer-managed-kms-key/
Blog — Self-managed Active Directory with Kerberos for RDS for Db2: https://aws.amazon.com/blogs/database/use-kerberos-authentication-with-a-self-managed-active-directory-for-amazon-rds-for-db2/
Related topics (cited resources, not yet routed sub-skills)
These adjacent topics are not expanded into routed references in this iteration. Each is discoverable through the cited resource below.
Reverse log shipping for RDS for Db2 (DBBLOG-5352): https://aws.amazon.com/blogs/database/implement-reverse-log-shipping-for-amazon-rds-for-db2/
Multi-account connectivity: workspace source
04-db2-client/RDS-Db2-Multiple-Account-Connectivity/Terraform provisioning: workspace source
04-db2-client/RDS-Db2-Terraform/CIS compliance: workspace source
04-db2-client/CIS-Compliance/db2mon monitoring: workspace source
04-db2-client/db2mon_RDS/Compression savings: workspace source
04-db2-client/Compression-Savings/Migration prerequisite check (DBBLOG-5048): https://aws.amazon.com/blogs/database/migrate-from-ibm-db2-to-amazon-rds-for-db2-using-a-migration-prerequisite-check/
Load from S3: workspace source
04-db2-client/load-from-s3/Sample Java stored procedures: workspace source
04-db2-client/sample-java-sp/
Blog catalog
The authoritative list of published RDS for Db2 blogs and sample tools is maintained at https://github.com/aws-samples/sample-rds-db2-tools/tree/main — consult it for the current set of blog articles and companion code.
Related skill (migrating off Db2 LUW to PostgreSQL):
rds-postgres-migration(if present in corpus).
Handoff from aws-database-selection
This skill can be invoked directly, or it can be entered from the aws-database-selection parent skill after that skill has run a requirements interview and produced a requirements.json artifact. When you see a backtick-wrapped path matching aws_dbs_requirements/*/requirements.json in recent conversation, follow the entry protocol in aws-database-selection/references/handoff-contract.md:
Read the artifact using
file_read.Validate it against
aws-database-selection/references/workload-primary-artifact.schema.json. If malformed or unreadable, tell the user and proceed without it.Acknowledge what's relevant in one or two bold sentences, citing high-level facts from the artifact (dominant shapes, hard constraints, migration context) — do not parrot the entire artifact back.
Scope-check: this skill is scoped to Amazon RDS for Db2 — migrations from Db2 z/OS or LUW, HADR, standby replicas, SQL PL routines, Q Replication cutovers. If the artifact's
workload_primaries.dominant_shapesormigration_contextdon't match that scope, emit weak backpressure per the handoff contract: suggestamazon-aurorafor refactor-to-PostgreSQL from Db2, or go back toaws-database-selectionif Db2 isn't the source, then ask the user whether to go back or proceed anyway. Do not silently misuse the artifact.Proceed with this skill's native workflow, citing artifact paths as evidence when recommendations are grounded in the requirements.
The curated RDS-for-Db2 selection facts that the parent aws-database-selection skill consumes live at assets/selection-knowledge-input.json (with a human-readable companion at assets/selection-knowledge-input.md). These capture the in-scope source-migration scenarios, hard constraints, HA/DR options, and security areas in a structured, reusable form — read them when you need the curated selection view rather than re-deriving it.
All user-facing output from this skill follows the markdown-primitives-only formatting convention in the handoff contract: bold labels, backticks for paths and enum values, bullet lists for alternatives, no ASCII art or box-drawing characters.
Files
- ATTRIBUTION.mdAdded by AutoGineer902 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer67 B
- SKILL.md27.0 KiB
- assets/README.md963 B
- assets/rds-db2-minimal-iam-policy.json9.0 KiB
- assets/rds-db2-trust-policy.json317 B
- assets/selection-knowledge-input.json1.3 KiB
- assets/selection-knowledge-input.md2.4 KiB
- references/ad-kerberos.md6.4 KiB
- references/backup-restore.md6.4 KiB
- references/byok-kms.md6.7 KiB
- references/code-page-collation.md6.2 KiB
- references/colocation.md7.3 KiB
- references/connection-drivers.md4.3 KiB
- references/connectivity-tls.md3.1 KiB
- references/connectivity.md8.6 KiB
- references/db2-audit.md6.5 KiB
- references/ha-dr.md5.2 KiB
- references/mainframe-migration.md5.2 KiB
- references/migration.md7.8 KiB
- references/minimum-iam.md7.0 KiB
- references/operations.md7.2 KiB
- references/provisioning.md7.1 KiB
- scripts/Db2KerberosConnection.javaScript8.6 KiB
- scripts/Db2SslTest.javaScript6.3 KiB
- scripts/create-db2-audit-role.shScript4.5 KiB
- scripts/db2-driver.shScript19.8 KiB
- scripts/db2-kerberos-test.shScript10.6 KiB
- scripts/db2client-airgap.shScript14.1 KiB
- scripts/db2client-configure.shScript38.5 KiB
- scripts/functions.shScript18.6 KiB
scripts/db2client-configure.sh
#!/usr/bin/env bash
# =============================================================================
# db2client-configure.sh — Configure db2dsdriver.cfg for RDS DB2 RT client
# =============================================================================
# Run as db2inst1 after db2-driver.sh has installed the RT client:
#
# sudo su - db2inst1
# REGION=<region> source db2client-configure.sh # online
# BUCKET=db2client-artifacts-<account>-<region> REGION=<region> source db2client-configure.sh # airgap
#
# Optional env vars:
# DB_INSTANCE_ID=<id> target a specific RDS instance
# PROFILE=<profile> AWS CLI profile (default: default)
# =============================================================================
if [ -z "$BASH_VERSION" ]; then exec bash "$0" "$@"; fi
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; NC='\033[0m'
log_info() { echo -e "${BLUE}[ INFO]${NC} $(date '+%H:%M:%S') - $1" >&2; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%H:%M:%S') - $1" >&2; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $(date '+%H:%M:%S') - $1" >&2; }
log_error() { echo -e "${RED}[ ERROR]${NC} $(date '+%H:%M:%S') - $1" >&2; }
# RDS cert bundle URL — partition-aware (commercial / GovCloud / China)
rds_truststore_url() {
local region="$1"
case "$region" in
us-gov-*) echo "https://truststore.pki.${region}.rds.amazonaws.com/${region}/${region}-bundle.pem" ;;
cn-*) echo "https://truststore.pki.${region}.rds.amazonaws.com.cn/${region}/${region}-bundle.pem" ;;
*) echo "https://truststore.pki.rds.amazonaws.com/${region}/${region}-bundle.pem" ;;
esac
}
# =============================================================================
# Kerberos / domain-join detection
# =============================================================================
# Sets IS_DOMAIN_JOINED=true and KRB_REALM=<realm> when the host is confirmed
# to be a member of an Active Directory / Kerberos realm.
#
# Detection order (first match wins):
# 1. 'realm list' shows "configured: kerberos-member" (realmd + sssd — most common)
# 2. /etc/krb5.conf contains a default_realm (any kerberos setup)
#
# When domain-joined, also validates that a TGT exists in the Kerberos cache.
# RDS for Db2 does not support local user authentication when Kerberos is
# enabled — a valid TGT is required for ALL connections (including the
# internal bootstrap query). The script exits if no ticket is found.
#
IS_DOMAIN_JOINED=false
KRB_REALM=""
detect_domain_join() {
# Method 1: realm list (realmd)
if command -v realm &>/dev/null; then
local realm_out
realm_out=$(realm list 2>/dev/null)
if echo "$realm_out" | grep -q "configured: kerberos-member"; then
KRB_REALM=$(echo "$realm_out" | awk '/^[^ ]/ {realm=$1} /configured: kerberos-member/ {print realm; exit}')
log_info "Domain join detected via 'realm list' — realm: $KRB_REALM"
# Only treat the host as domain-joined if a valid TGT is present. Otherwise
# the Kerberos DSNs would be written but fail at connect time.
if _require_tgt; then
IS_DOMAIN_JOINED=true
else
log_warning "Domain join detected but no valid TGT — Kerberos DSNs will NOT be created"
IS_DOMAIN_JOINED=false
fi
return
fi
fi
# Method 2: /etc/krb5.conf default_realm
if [ -f /etc/krb5.conf ]; then
local realm_line
realm_line=$(grep -i '^\s*default_realm\s*=' /etc/krb5.conf 2>/dev/null | head -1)
if [ -n "$realm_line" ]; then
KRB_REALM=$(echo "$realm_line" | awk -F'=' '{gsub(/[[:space:]]/,"",$2); print $2}')
log_info "Domain join detected via /etc/krb5.conf — realm: $KRB_REALM"
# Only treat the host as domain-joined if a valid TGT is present. Otherwise
# the Kerberos DSNs would be written but fail at connect time.
if _require_tgt; then
IS_DOMAIN_JOINED=true
else
log_warning "Domain join detected but no valid TGT — Kerberos DSNs will NOT be created"
IS_DOMAIN_JOINED=false
fi
return
fi
fi
log_info "No domain join detected — Kerberos DSN parameters will not be added"
}
# Gate: verify a valid TGT exists. Called only when IS_DOMAIN_JOINED=true.
# Both local auth and Kerberos SSL DSNs will be written on domain-joined hosts.
# A valid TGT is required for the Kerberos DSNs and for the bootstrap connect
# when db2comm=SSL (since local auth over SSL also needs a working SSL path
# that the Kerberos ticket provides for discovery).
_require_tgt() {
if ! command -v klist &>/dev/null; then
log_error "klist not found — cannot verify Kerberos ticket."
log_error "Install krb5-workstation (AL2/AL2023) and obtain a ticket:"
log_error " sudo dnf install -y krb5-workstation"
log_error " kinit $(whoami)@${KRB_REALM}"
return 1
fi
if ! klist -s 2>/dev/null; then
log_error "============================================================="
log_error "This host is domain-joined (realm: $KRB_REALM)."
log_error "RDS for Db2 does not support local user authentication"
log_error "when Kerberos is enabled — a valid TGT is required."
log_error ""
log_error "No Kerberos ticket found in the cache. Obtain one first:"
log_error " kinit $(whoami)@${KRB_REALM}"
log_error " klist # confirm ticket is present"
log_error " REGION=$REGION source db2client-configure.sh"
log_error "============================================================="
return 1
fi
# Ticket exists — show the principal so the user can confirm it's the right one
local principal
principal=$(klist 2>/dev/null | awk '/^Default principal:/ {print $3}')
log_success "Kerberos TGT found — principal: ${principal:-<unknown>}"
}
# --- Defaults ---
PROFILE=${PROFILE:-"default"}
DB2USER_NAME=${DB2USER_NAME:-"db2inst1"}
DB_NAMES_INPUT=${DB_NAMES:-""} # optional: comma-separated list, e.g. DB_NAMES=DB2DB,MYDB
E_URL=${E_URL:-""} # optional: custom RDS endpoint, e.g.
# E_URL="--endpoint-url https://rds-siteb.us-east-1.amazonaws.com --no-verify-ssl"
SSL_CERT_FILE="" # set by download_pem_file() — do not set manually
declare -a HELP_COMMANDS=()
declare -a DB_INSTANCES=()
declare -a MASTER_USER_NAMES=()
declare -a MASTER_USER_PASSWORDS=()
declare -a DB_NAMES=()
# Wrapper for all 'aws rds' calls — injects E_URL when set.
# Usage: aws_rds describe-db-instances --region ... --query ... --output text
aws_rds() {
# shellcheck disable=SC2086
aws rds "$@" ${E_URL}
}
# =============================================================================
# Validation
# =============================================================================
validate() {
if [ -z "$REGION" ]; then
log_error "REGION is required. Example: BUCKET=... REGION=us-east-1 source db2client-configure.sh"
return 1
fi
# BUCKET is optional — only needed for airgap SSL cert download
if [ "$(whoami)" != "$DB2USER_NAME" ]; then
log_error "This script must be run as $DB2USER_NAME. Run: sudo su - $DB2USER_NAME"
return 1
fi
if [ ! -d "$HOME/sqllib" ]; then
log_error "RT client not installed — $HOME/sqllib not found. Run db2-driver.sh as root first."
return 1
fi
}
# =============================================================================
# Credentials
# =============================================================================
set_credentials() {
# CloudShell
if curl -s --connect-timeout 1 http://127.0.0.1:1338/latest/meta-data/ >/dev/null 2>&1; then
log_info "Detected AWS CloudShell environment"
local token creds
token=$(curl -sX PUT "http://127.0.0.1:1338/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
creds=$(curl -s -H "Authorization: $token" "http://127.0.0.1:1338/latest/meta-data/container/security-credentials")
export AWS_ACCESS_KEY_ID=$(echo "$creds" | jq -r .AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo "$creds" | jq -r .SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo "$creds" | jq -r .Token)
log_success "AWS credentials set from CloudShell"
return
fi
# EC2 IMDSv2
if curl -s --connect-timeout 1 http://169.254.169.254/latest/meta-data/ >/dev/null 2>&1; then
log_info "Detected EC2 environment"
local token role creds
token=$(curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
role=$(curl -s -H "X-aws-ec2-metadata-token: $token" http://169.254.169.254/latest/meta-data/iam/security-credentials/)
creds=$(curl -s -H "X-aws-ec2-metadata-token: $token" "http://169.254.169.254/latest/meta-data/iam/security-credentials/$role")
export AWS_ACCESS_KEY_ID=$(echo "$creds" | jq -r .AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo "$creds" | jq -r .SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo "$creds" | jq -r .Token)
log_success "AWS credentials set from EC2 instance role"
return
fi
# Fall back to configured profile
if [ -n "${AWS_ACCESS_KEY_ID:-}" ] && [ -n "${AWS_SECRET_ACCESS_KEY:-}" ]; then
# SECURITY: AWS_ACCESS_KEY_ID/SECRET are long-lived static keys — acceptable
# only for temporary CI/CD automation, NEVER for production. Production
# workflows MUST obtain credentials through a CloudShell/EC2 IAM role (handled
# above) or a configured profile, never hard-coded or long-lived keys.
log_info "Using AWS credentials from environment variables"
else
log_info "Using AWS CLI profile: $PROFILE"
export AWS_PROFILE="$PROFILE"
fi
}
# =============================================================================
# Instance discovery
# =============================================================================
list_db_instances() {
local query='DBInstances[?starts_with(Engine, `db2`)].DBInstanceIdentifier'
local aws_output
aws_output=$(aws_rds describe-db-instances \
--region "$REGION" \
--query "$query" \
--output text 2>/dev/null)
local existing_instances=($aws_output)
if [ ${#existing_instances[@]} -eq 0 ]; then
log_error "No DB2 instances found in region $REGION"
return 1
fi
if [ -n "${DB_INSTANCE_ID:-}" ]; then
if [ "$DB_INSTANCE_ID" = "ALL" ]; then
DB_INSTANCES=("${existing_instances[@]}")
log_info "Processing ALL DB2 instances: ${DB_INSTANCES[*]}"
return 0
fi
DB_INSTANCES=("$DB_INSTANCE_ID")
log_info "Using specified instance: $DB_INSTANCE_ID"
return 0
fi
if [ ${#existing_instances[@]} -eq 1 ]; then
DB_INSTANCES=("${existing_instances[0]}")
log_info "Auto-selected only available instance: ${existing_instances[0]}"
return 0
fi
# Interactive selection — one instance only
local choice=-1
while [ "$choice" -lt 1 ] || [ "$choice" -gt ${#existing_instances[@]} ]; do
echo "Available DB2 instances:" >&2
for i in "${!existing_instances[@]}"; do
echo "$((i+1)). ${existing_instances[$i]}" >&2
done
read -p "Select instance (1-${#existing_instances[@]}): " choice
if [ "$choice" -ge 1 ] && [ "$choice" -le ${#existing_instances[@]} ]; then
DB_INSTANCES=("${existing_instances[$((choice-1))]}")
else
log_warning "Invalid choice"
choice=-1
fi
done
}
# =============================================================================
# Master user names and passwords
# =============================================================================
get_all_master_user_names() {
MASTER_USER_NAMES=()
for db_instance in "${DB_INSTANCES[@]}"; do
local name
name=$(aws_rds describe-db-instances \
--db-instance-identifier "$db_instance" \
--region "$REGION" \
--query "DBInstances[0].MasterUsername" \
--output text 2>/dev/null)
[ "$name" = "None" ] && name=""
MASTER_USER_NAMES+=("$name")
log_info "Master user for $db_instance: ${name:-<not found>}"
done
}
get_all_master_passwords() {
MASTER_USER_PASSWORDS=()
local password_file="$HOME/.need_password"
for db_instance in "${DB_INSTANCES[@]}"; do
local secret_arn
secret_arn=$(aws_rds describe-db-instances \
--db-instance-identifier "$db_instance" \
--region "$REGION" \
--query "DBInstances[0].MasterUserSecret.SecretArn" \
--output text 2>/dev/null)
if [ -n "$secret_arn" ] && [ "$secret_arn" != "None" ]; then
local secret_json password
secret_json=$(aws secretsmanager get-secret-value \
--secret-id "$secret_arn" \
--region "$REGION" \
--query "SecretString" \
--output text 2>/dev/null)
password=$(jq -r '.password' <<< "$secret_json")
if [ -n "$password" ]; then
log_info "Retrieved password from Secrets Manager for $db_instance"
MASTER_USER_PASSWORDS+=("$password")
continue
fi
fi
# Fall back to .need_password file. This is a DEVELOPMENT/TEST-ONLY path for
# instances not using Secrets Manager. The file MUST be created with
# `chmod 600 ~/.need_password` (owner read/write only) and MUST NEVER be
# committed to version control or shared. For production, provision with
# --manage-master-user-password so RDS stores and rotates the master
# credential in Secrets Manager instead of keeping plaintext on disk.
local file_password=""
if [ -f "$password_file" ]; then
file_password=$(grep "^$db_instance " "$password_file" 2>/dev/null | cut -d' ' -f2-)
fi
if [ -n "$file_password" ] && [ "$file_password" != "replace this with the master user password" ]; then
log_warning "Using password from $password_file for $db_instance (dev/test only — use --manage-master-user-password in production)"
MASTER_USER_PASSWORDS+=("$file_password")
else
log_warning "No password found for $db_instance — prompting"
read -rsp "Password for $db_instance: " entered_password; echo
MASTER_USER_PASSWORDS+=("${entered_password:-}")
fi
done
}
# =============================================================================
# Database name discovery
# =============================================================================
#
# Resolution order:
# 1. DB_NAMES env var — comma-separated list, e.g. DB_NAMES=DB2DB,MYDB
# (useful for automation or when RDSADMIN is inaccessible)
# 2. DBName field on the RDS instance (single-database, most common case)
# 3. Bootstrap connect to RDSADMIN + rdsadmin.list_databases()
# — requires the connecting user to have CONNECT on RDSADMIN
# — when domain-joined, this uses the Kerberos TGT (no master user/password)
# — when Kerberos is active but the AD user lacks RDSADMIN access, this
# step fails and the script falls through to the interactive prompt
# 4. Interactive prompt — user enters names manually; skipped when stdin
# is not a terminal (non-interactive mode)
#
get_all_database_names() {
local db_instance_id="$1" master_user="$2" master_password="$3" temp_dsn="${4:-RDSADMIN}"
DB_NAMES=()
# --- Resolution 1: DB_NAMES env var ---
if [ -n "${DB_NAMES_INPUT:-}" ]; then
IFS=',' read -ra DB_NAMES <<< "$DB_NAMES_INPUT"
# Trim whitespace from each entry
DB_NAMES=("${DB_NAMES[@]// /}")
log_info "Using database list from DB_NAMES env var: ${DB_NAMES[*]}"
return 0
fi
# --- Resolution 2: DBName field on the RDS instance ---
local default_dbname
default_dbname=$(aws_rds describe-db-instances \
--db-instance-identifier "$db_instance_id" \
--region "$REGION" \
--query "DBInstances[0].DBName" \
--output text 2>/dev/null)
[ "$default_dbname" = "None" ] && default_dbname=""
if [ -n "$default_dbname" ]; then
log_info "Default database from RDS metadata: $default_dbname"
DB_NAMES=("$default_dbname")
return 0
fi
# --- Resolution 3: Bootstrap connect to RDSADMIN ---
log_info "No default database set — attempting RDSADMIN bootstrap query"
local connect_out connect_rc
if [ "${IS_DOMAIN_JOINED:-false}" = "true" ]; then
connect_out=$(db2 "connect to $temp_dsn" 2>&1)
connect_rc=$?
else
connect_out=$(db2 "connect to $temp_dsn user $master_user using '$master_password'" 2>&1)
connect_rc=$?
fi
if [ $connect_rc -eq 0 ]; then
local db_names_raw
mapfile -t db_names_raw < <(
db2 -x "SELECT database_name FROM TABLE(rdsadmin.list_databases()) WHERE UPPER(database_name) <> 'RDSADMIN'" 2>/dev/null
)
db2 connect reset >/dev/null 2>&1 || true
local db_names_clean=()
for dbname in "${db_names_raw[@]}"; do
dbname="$(echo "$dbname" | xargs)"
[[ -n "$dbname" && ! "$dbname" =~ ^SQL ]] && db_names_clean+=("$dbname")
done
if [ ${#db_names_clean[@]} -gt 0 ]; then
DB_NAMES=("${db_names_clean[@]}")
log_info "Found ${#DB_NAMES[@]} database(s) via RDSADMIN: ${DB_NAMES[*]}"
return 0
fi
log_warning "RDSADMIN connect succeeded but no user databases found"
else
db2 connect reset >/dev/null 2>&1 || true
log_warning "RDSADMIN bootstrap connect failed (rc=$connect_rc)"
if [ "${IS_DOMAIN_JOINED:-false}" = "true" ]; then
local principal
principal=$(klist 2>/dev/null | awk '/^Default principal:/ {print $3}')
log_warning "Kerberos principal '${principal}' may not have CONNECT privilege on RDSADMIN."
log_warning "This is expected — RDSADMIN is protected and AD users are not granted access by default."
fi
fi
# --- Resolution 4: Interactive prompt ---
log_info "------------------------------------------------------------"
log_info "Cannot discover databases automatically for $db_instance_id."
log_info "To skip this prompt next time, set before running:"
log_info " DB_NAMES=DB2DB,MYDB REGION=$REGION source db2client-configure.sh"
log_info "------------------------------------------------------------"
if [ -t 0 ]; then
local input
read -rp "Enter database name(s) for $db_instance_id (comma-separated, or Enter to skip): " input
if [ -n "$input" ]; then
IFS=',' read -ra DB_NAMES <<< "$input"
DB_NAMES=("${DB_NAMES[@]// /}")
log_info "Registering databases: ${DB_NAMES[*]}"
return 0
fi
log_warning "No databases entered — only the RDSDBSSL admin DSN will be created for $db_instance_id"
else
log_warning "Non-interactive mode and no DB_NAMES set — only the admin DSN will be created"
log_warning "Re-run with: DB_NAMES=<name1,name2> REGION=$REGION source db2client-configure.sh"
fi
return 0 # not fatal — admin DSN is still useful
}
# =============================================================================
# DSN helpers
# =============================================================================
#
# Naming convention (all aliases must be ≤ 8 characters):
#
# Admin database (RDSADMIN):
# RDSAT — TCP, local auth (SERVER_ENCRYPT)
# RDSAS — SSL, local auth
# RDSAKS — SSL, Kerberos
#
# User databases (<DB>, truncated to fit):
# <DB>T — TCP, local auth
# <DB>S — SSL, local auth
# <DB>SK — SSL, Kerberos
#
# Multi-instance: numeric index appended before the type suffix,
# e.g. RDSAT0 / RDSAT1, DB2DB0T / DB2DB0S / DB2DB0SK
#
# generate_db_alias NAME SUFFIX [INSTANCE_SUFFIX]
# Builds a user-DB alias that fits in 8 chars including BOTH the type suffix
# and the optional multi-instance index, e.g. generate_db_alias DB2DB SK 0 -> DB2DB0SK.
# The instance index is placed before the type suffix (matching the documented
# DB2DB0SK convention) and is counted against the 8-char budget so callers must
# NOT append ${SUFFIX} themselves.
# SUFFIX = T | S | SK (1-2 chars); INSTANCE_SUFFIX = "" | 0 | 1 | ...
generate_db_alias() {
local raw="${1^^}" suffix="${2}" instance_suffix="${3:-}"
local maxbase=$(( 8 - ${#suffix} - ${#instance_suffix} ))
(( maxbase < 0 )) && maxbase=0
local base="${raw:0:$maxbase}"
echo "${base}${instance_suffix}${suffix}"
}
writecfg_tcp() {
local dsn=$1 dbname=$2 host=$3 port=$4
db2cli writecfg add -dsn "$dsn" -database "$dbname" -host "$host" -port "$port" \
-parameter "Authentication=SERVER_ENCRYPT"
}
# SSL + local auth (SERVER_ENCRYPT)
writecfg_ssl_local() {
local dsn=$1 dbname=$2 host=$3 port=$4
local cert_file="${SSL_CERT_FILE:-$HOME/$REGION-bundle.pem}"
db2cli writecfg add -dsn "$dsn" -database "$dbname" -host "$host" -port "$port" \
-parameter "SSLServerCertificate=${cert_file};SecurityTransportMode=SSL;TLSVersion=TLSV12"
}
# SSL + Kerberos
writecfg_ssl_krb() {
local dsn=$1 dbname=$2 host=$3 port=$4
local cert_file="${SSL_CERT_FILE:-$HOME/$REGION-bundle.pem}"
db2cli writecfg add -dsn "$dsn" -database "$dbname" -host "$host" -port "$port" \
-parameter "Authentication=KERBEROS;KRBPlugin=IBMkrb5;SSLServerCertificate=${cert_file};SecurityTransportMode=SSL;TLSVersion=TLSV12"
}
# =============================================================================
# Read parameter group values for a given instance
# Returns the ParameterValue or "" if not found / None
# =============================================================================
get_param_group_name() {
# Sets global PARAM_GROUP for the current DB_INSTANCE_IDENTIFIER
PARAM_GROUP=$(aws_rds describe-db-instances \
--db-instance-identifier "$DB_INSTANCE_IDENTIFIER" \
--region "$REGION" \
--query "DBInstances[0].DBParameterGroups[0].DBParameterGroupName" \
--output text 2>/dev/null)
[ "$PARAM_GROUP" = "None" ] && PARAM_GROUP=""
}
get_param_value() {
local param_name="$1"
[ -z "${PARAM_GROUP:-}" ] && echo "" && return
local val
val=$(aws_rds describe-db-parameters \
--db-parameter-group-name "$PARAM_GROUP" \
--region "$REGION" \
--query "Parameters[?ParameterName=='${param_name}'].ParameterValue" \
--output text 2>/dev/null)
[ "$val" = "None" ] && val=""
echo "$val"
}
get_ssl_port() {
get_param_value "ssl_svcename"
}
# Returns the db2comm value from the parameter group (e.g. "SSL", "TCPIP", "TCPIP,SSL")
get_db2comm() {
local raw
raw=$(get_param_value "db2comm")
# Normalise: upper-case, strip spaces
echo "${raw^^}" | tr -d ' '
}
# True when db2comm contains TCPIP (and so TCP connections are allowed)
db2comm_has_tcpip() {
local comm="$1"
[[ "$comm" == *"TCPIP"* ]]
}
# True when db2comm is set to SSL-only (no TCPIP)
db2comm_ssl_only() {
local comm="$1"
[[ "$comm" == "SSL" ]]
}
download_pem_file() {
# Sets global SSL_CERT_FILE to the path of the cert Db2 should trust.
#
# Standard endpoint (E_URL not set):
# Downloads <region>-bundle.pem from the public RDS truststore.
# The bundle is reordered so RSA2048 is first (Db2 CLP requirement).
#
# Custom endpoint (E_URL set — PrivateLink, siteb, internal domain):
# The server presents a cert signed by an internal/Preprod CA that is
# NOT in the public RDS bundle. Instead, the root CA is extracted live
# from the server's TLS chain and saved as <region>-siteb-root-ca.pem.
# Only the root is needed — GSKit walks the chain from root to leaf.
if [ -n "${E_URL:-}" ]; then
_download_pem_custom_endpoint "$@"
else
_download_pem_standard "$@"
fi
}
_download_pem_standard() {
local pem_file="$HOME/$REGION-bundle.pem"
SSL_CERT_FILE="$pem_file"
if [ -f "$pem_file" ]; then
log_info "SSL certificate already present: $pem_file"
return 0
fi
if [ -n "${BUCKET:-}" ]; then
log_info "Downloading SSL certificate from s3://$BUCKET/ssl/$REGION-bundle.pem ..."
aws s3 cp "s3://$BUCKET/ssl/$REGION-bundle.pem" "$pem_file" \
--region "$REGION" --quiet
else
local url
url=$(rds_truststore_url "$REGION")
log_info "Downloading SSL certificate from $url ..."
curl -sL "$url" -o "$pem_file"
fi
if [ $? -ne 0 ]; then
log_error "Failed to download SSL certificate"
return 1
fi
# Reorder certificates so RSA2048 is first.
# Db2 CLP picks the first cert in the bundle for the TLS handshake.
# RDS for Db2 only has RSA2048 — if RSA4096 is first (e.g. us-west-1)
# the CLP connection fails. Python/JCC drivers iterate all certs so
# they are unaffected. This reorder is a no-op for regions where
# RSA2048 is already first (e.g. us-east-1).
if command -v openssl &>/dev/null; then
local tmp_pem; tmp_pem=$(mktemp)
awk '
/-----BEGIN CERTIFICATE-----/ { cert=""; in_cert=1 }
in_cert { cert = cert $0 "\n" }
/-----END CERTIFICATE-----/ { certs[++n] = cert; in_cert=0 }
END {
first=""; rest=""
for (i=1; i<=n; i++) {
cmd = "echo \"" certs[i] "\" | openssl x509 -noout -subject 2>/dev/null"
cmd | getline subj; close(cmd)
if (subj ~ /RSA2048/) { first = certs[i] }
else { rest = rest certs[i] }
}
printf "%s%s", first, rest
}
' "$pem_file" > "$tmp_pem"
if [ -s "$tmp_pem" ]; then
mv -f "$tmp_pem" "$pem_file"
log_info "SSL cert reordered: RSA2048 first (Db2 CLP compatibility)"
else
rm -f "$tmp_pem"
log_warning "SSL cert reorder skipped — openssl subject parse returned empty"
fi
else
log_warning "openssl not found — skipping cert reorder (Db2 CLP may fail on regions where RSA2048 is not first)"
fi
log_success "SSL certificate saved to $pem_file"
}
_download_pem_custom_endpoint() {
# For custom/internal endpoints the server presents a cert signed by an
# internal CA (e.g. Amazon RDS Preprod Root CA) that is not in the public
# RDS bundle. Extract the root CA directly from the live TLS chain.
#
# The DB_ADDRESS global must be set before this is called (set in configure_dsn).
local root_ca_file="$HOME/$REGION-siteb-root-ca.pem"
SSL_CERT_FILE="$root_ca_file"
if [ -f "$root_ca_file" ]; then
log_info "Custom endpoint root CA already present: $root_ca_file"
return 0
fi
if [ -z "${DB_ADDRESS:-}" ]; then
log_error "DB_ADDRESS not set — cannot extract root CA from custom endpoint"
return 1
fi
if ! command -v openssl &>/dev/null; then
log_error "openssl not found — required to extract root CA from custom endpoint"
return 1
fi
log_info "Custom endpoint detected (E_URL set) — extracting root CA from TLS chain ..."
log_info "Connecting to $DB_ADDRESS:${SSL_PORT:-50443} ..."
# Pull full chain, skip the leaf (cert #1), save intermediate + root
local full_chain
full_chain=$(openssl s_client \
-connect "${DB_ADDRESS}:${SSL_PORT:-50443}" \
-showcerts \
2>/dev/null </dev/null)
if [ -z "$full_chain" ]; then
log_error "Could not retrieve TLS chain from $DB_ADDRESS:${SSL_PORT:-50443}"
return 1
fi
# Extract root CA — the last self-signed cert in the chain
# (issuer == subject). Works for chains of any depth.
echo "$full_chain" | awk '
/-----BEGIN CERTIFICATE-----/ { n++; cert="" }
{ cert = cert $0 "\n" }
/-----END CERTIFICATE-----/ { certs[n] = cert }
END { print certs[n] }
' > "$root_ca_file"
if [ ! -s "$root_ca_file" ]; then
log_error "Failed to extract root CA from TLS chain"
rm -f "$root_ca_file"
return 1
fi
# Verify it's actually self-signed (root CA)
local issuer subject
issuer=$(openssl x509 -noout -issuer -in "$root_ca_file" 2>/dev/null | sed 's/issuer=//')
subject=$(openssl x509 -noout -subject -in "$root_ca_file" 2>/dev/null | sed 's/subject=//')
if [ "$issuer" != "$subject" ]; then
log_warning "Extracted cert may not be a root CA (issuer != subject)"
log_warning "issuer: $issuer"
log_warning "subject: $subject"
fi
log_success "Root CA extracted: $root_ca_file"
log_info " Subject: $subject"
}
build_connect_help_rt() {
local alias_name=$1 db_name=$2 use_kerberos=${3:-false}
if [ "$use_kerberos" = "true" ]; then
HELP_COMMANDS+=("db2 \"connect to ${alias_name}\" # ${db_name}")
else
HELP_COMMANDS+=("db2 \"connect to ${alias_name} user ${MASTER_USER_NAME} using '\$MASTER_USER_PASSWORD'\" # ${db_name}")
fi
}
print_all_help() {
[ ${#HELP_COMMANDS[@]} -eq 0 ] && return
echo ""
echo " ========================="
echo " db2 terminate"
for c in "${HELP_COMMANDS[@]}"; do echo " $c"; done
echo " ========================="
echo ""
}
# =============================================================================
# Main DSN configuration
# =============================================================================
configure_dsn() {
log_info "============================================================================"
log_info "Creating DB2 RT DSN entries for RDS DB2 instance(s)"
log_info "Region: $REGION"
log_info "============================================================================"
detect_domain_join
list_db_instances || return 1
get_all_master_user_names
get_all_master_passwords
# Clean slate before writing any DSN entries
rm -f "$HOME/sqllib/cfg/db2dsdriver.cfg"
for i in "${!DB_INSTANCES[@]}"; do
local DB_INSTANCE_IDENTIFIER="${DB_INSTANCES[$i]}"
local MASTER_USER_NAME="${MASTER_USER_NAMES[$i]}"
local MASTER_USER_PASSWORD="${MASTER_USER_PASSWORDS[$i]}"
local SUFFIX; [ ${#DB_INSTANCES[@]} -eq 1 ] && SUFFIX="" || SUFFIX="$i"
log_info "============================================================================"
log_info "Processing: $DB_INSTANCE_IDENTIFIER"
[ -z "$MASTER_USER_NAME" ] && log_error "No master user for $DB_INSTANCE_IDENTIFIER — skipping" && continue
[ -z "$MASTER_USER_PASSWORD" ] && log_warning "No password for $DB_INSTANCE_IDENTIFIER — skipping" && continue
local DB_ADDRESS DB_TCP_IP_PORT
DB_ADDRESS=$(aws_rds describe-db-instances \
--db-instance-identifier "$DB_INSTANCE_IDENTIFIER" \
--region "$REGION" \
--query "DBInstances[0].Endpoint.Address" \
--output text 2>/dev/null)
DB_TCP_IP_PORT=$(aws_rds describe-db-instances \
--db-instance-identifier "$DB_INSTANCE_IDENTIFIER" \
--region "$REGION" \
--query "DBInstances[0].Endpoint.Port" \
--output text 2>/dev/null)
[ -z "$DB_ADDRESS" ] && log_error "No endpoint for $DB_INSTANCE_IDENTIFIER — skipping" && continue
# -----------------------------------------------------------------------
# Read parameter group values for this instance
# -----------------------------------------------------------------------
get_param_group_name # sets $PARAM_GROUP
local DB2COMM SSL_PORT
DB2COMM=$(get_db2comm)
SSL_PORT=$(get_ssl_port)
# Default to TCPIP if db2comm is not set in the parameter group
[ -z "$DB2COMM" ] && DB2COMM="TCPIP"
log_info "db2comm : ${DB2COMM} | ssl_svcename : ${SSL_PORT:-<not set>}"
local WANT_TCP=false WANT_SSL=false
db2comm_has_tcpip "$DB2COMM" && WANT_TCP=true
[ -n "$SSL_PORT" ] && WANT_SSL=true
if [ "$WANT_SSL" = "false" ] && [ "$WANT_TCP" = "false" ]; then
log_warning "Neither TCPIP port nor ssl_svcename configured for $DB_INSTANCE_IDENTIFIER — skipping"
continue
fi
# -----------------------------------------------------------------------
# Bootstrap: write a temporary DSN to discover database names.
# Use SSL (local auth) when db2comm is SSL-only; otherwise use TCP.
# -----------------------------------------------------------------------
local TEMP_DSN="RDSTMP${SUFFIX}"
if [ "$WANT_TCP" = "true" ]; then
writecfg_tcp "$TEMP_DSN" "RDSADMIN" "$DB_ADDRESS" "$DB_TCP_IP_PORT" >/dev/null 2>&1
else
# SSL-only — download cert first (sets SSL_CERT_FILE)
if ! download_pem_file; then
log_error "Cannot download SSL cert for $DB_INSTANCE_IDENTIFIER — skipping"
continue
fi
# Bootstrap always uses local auth — Kerberos DSNs are written after discovery
writecfg_ssl_local "$TEMP_DSN" "RDSADMIN" "$DB_ADDRESS" "$SSL_PORT" >/dev/null 2>&1
fi
# Fetch database names using the temporary DSN
get_all_database_names "$DB_INSTANCE_IDENTIFIER" "$MASTER_USER_NAME" "$MASTER_USER_PASSWORD" "$TEMP_DSN" || true
log_info "Databases to register: ${DB_NAMES[*]:-<none found>}"
# Remove temp DSN — final entries written below
db2cli writecfg remove -dsn "$TEMP_DSN" >/dev/null 2>&1 || true
# -----------------------------------------------------------------------
# Write TCP DSN entries (RDSAT / <DB>T)
# -----------------------------------------------------------------------
if [ "$WANT_TCP" = "true" ]; then
local tcp_admin_dsn="RDSAT${SUFFIX}"
log_info "Creating TCP DSN: $tcp_admin_dsn (local auth)"
writecfg_tcp "$tcp_admin_dsn" "RDSADMIN" "$DB_ADDRESS" "$DB_TCP_IP_PORT"
build_connect_help_rt "$tcp_admin_dsn" "RDSADMIN TCP"
for dbname in "${DB_NAMES[@]}"; do
local alias_t; alias_t="$(generate_db_alias "$dbname" "T" "$SUFFIX")"
log_info "Registering $dbname as $alias_t (TCP local)"
writecfg_tcp "$alias_t" "$dbname" "$DB_ADDRESS" "$DB_TCP_IP_PORT"
build_connect_help_rt "$alias_t" "$dbname TCP"
done
fi
# -----------------------------------------------------------------------
# Write SSL DSN entries (RDSAS / <DB>S and, when domain-joined, RDSAKS / <DB>SK)
# -----------------------------------------------------------------------
if [ "$WANT_SSL" = "true" ]; then
# Cert may already be downloaded in the bootstrap block above; idempotent
if ! download_pem_file; then
log_warning "SSL cert unavailable — skipping SSL entries for $DB_INSTANCE_IDENTIFIER"
else
log_info "SSL port: $SSL_PORT"
# --- SSL + local auth ---
local ssl_local_admin="RDSAS${SUFFIX}"
log_info "Creating SSL DSN: $ssl_local_admin (local auth)"
writecfg_ssl_local "$ssl_local_admin" "RDSADMIN" "$DB_ADDRESS" "$SSL_PORT"
build_connect_help_rt "$ssl_local_admin" "RDSADMIN SSL"
for dbname in "${DB_NAMES[@]}"; do
local alias_s; alias_s="$(generate_db_alias "$dbname" "S" "$SUFFIX")"
log_info "Registering $dbname as $alias_s (SSL local)"
writecfg_ssl_local "$alias_s" "$dbname" "$DB_ADDRESS" "$SSL_PORT"
build_connect_help_rt "$alias_s" "$dbname SSL"
done
# --- SSL + Kerberos (domain-joined only) ---
if [ "${IS_DOMAIN_JOINED:-false}" = "true" ]; then
log_info "Domain-joined host — also creating Kerberos SSL DSN entries"
local ssl_krb_admin="RDSAKS${SUFFIX}"
log_info "Creating SSL+Kerberos DSN: $ssl_krb_admin"
writecfg_ssl_krb "$ssl_krb_admin" "RDSADMIN" "$DB_ADDRESS" "$SSL_PORT"
build_connect_help_rt "$ssl_krb_admin" "RDSADMIN SSL+Kerberos" "true"
for dbname in "${DB_NAMES[@]}"; do
local alias_sk; alias_sk="$(generate_db_alias "$dbname" "SK" "$SUFFIX")"
log_info "Registering $dbname as $alias_sk (SSL Kerberos)"
writecfg_ssl_krb "$alias_sk" "$dbname" "$DB_ADDRESS" "$SSL_PORT"
build_connect_help_rt "$alias_sk" "$dbname SSL+Kerberos" "true"
done
fi
fi
fi
done
}
# =============================================================================
# Entry point
# =============================================================================
main() {
validate || return 1
set_credentials
configure_dsn || return 1
unset DB_INSTANCE_ID # clean up the user-supplied env var only AFTER configure_dsn has consumed it
print_all_help | tee "$HOME/CONN_HELP_README.txt" >&2
log_info "Run 'db2 terminate' then use the commands above (also saved to ~/CONN_HELP_README.txt)"
# Write instance registry (instance→DSN mapping, no passwords)
local registry="$HOME/.db2instances"
# Append or create entry for each instance
touch "$registry"
for i in "${!DB_INSTANCES[@]}"; do
local suffix; [ ${#DB_INSTANCES[@]} -eq 1 ] && suffix="" || suffix="$i"
# Determine which DSN names were written based on db2comm
DB_INSTANCE_IDENTIFIER="${DB_INSTANCES[$i]}"
get_param_group_name
local comm; comm=$(get_db2comm)
[ -z "$comm" ] && comm="TCPIP"
local tcp_dsn="" ssl_dsn="" krb_dsn=""
local ssl_port_val; ssl_port_val=$(get_ssl_port)
db2comm_has_tcpip "$comm" && tcp_dsn="RDSAT${suffix}"
[ -n "$ssl_port_val" ] && ssl_dsn="RDSAS${suffix}"
[ -n "$ssl_port_val" ] && [ "${IS_DOMAIN_JOINED:-false}" = "true" ] && krb_dsn="RDSAKS${suffix}"
# Remove existing entry for this instance then re-add
sed -i '' "/^${DB_INSTANCES[$i]}|/d" "$registry" 2>/dev/null || \
sed -i "/^${DB_INSTANCES[$i]}|/d" "$registry" 2>/dev/null || true
echo "${DB_INSTANCES[$i]}|${MASTER_USER_NAMES[$i]}|${tcp_dsn}|${ssl_dsn}|${krb_dsn}|${REGION}" >> "$registry"
done
chmod 600 "$registry"
log_success "Instance registry saved to $registry"
# Persist credentials for the last processed instance to ~/.db2env
# Uses printf %q to safely escape special characters in the password.
local last=$((${#DB_INSTANCES[@]} - 1))
export MASTER_USER_NAME="${MASTER_USER_NAMES[$last]}"
export MASTER_USER_PASSWORD="${MASTER_USER_PASSWORDS[$last]}"
# Default DSN priority: Kerberos SSL > local SSL > TCP
DB_INSTANCE_IDENTIFIER="${DB_INSTANCES[$last]}"
get_param_group_name
local last_comm; last_comm=$(get_db2comm)
[ -z "$last_comm" ] && last_comm="TCPIP"
local last_suffix; [ ${#DB_INSTANCES[@]} -eq 1 ] && last_suffix="" || last_suffix="$last"
local last_ssl_port; last_ssl_port=$(get_ssl_port)
if [ -n "$last_ssl_port" ] && [ "${IS_DOMAIN_JOINED:-false}" = "true" ]; then
export DB_DSN="RDSAKS${last_suffix}"
elif [ -n "$last_ssl_port" ]; then
export DB_DSN="RDSAS${last_suffix}"
else
export DB_DSN="RDSAT${last_suffix}"
fi
{
echo "export REGION=$(printf '%q' "$REGION")"
echo "export DB_INSTANCE_ID=$(printf '%q' "${DB_INSTANCES[$last]}")"
echo "export DB_DSN=$(printf '%q' "$DB_DSN")"
echo "export MASTER_USER_NAME=$(printf '%q' "${MASTER_USER_NAMES[$last]}")"
echo "export MASTER_USER_PASSWORD=$(printf '%q' "${MASTER_USER_PASSWORDS[$last]}")"
} > "$HOME/.db2env"
chmod 600 "$HOME/.db2env"
log_success "Credentials saved to ~/.db2env — auto-loaded by functions.sh"
log_success "DSN configuration complete. Connection help saved to ~/CONN_HELP_README.txt"
# Add source functions.sh to shell profile files if not already there
local source_line='source ~/functions.sh'
local comment='# DB2 helper functions'
for profile in "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
[ -f "$profile" ] || continue
if ! grep -q 'source ~/functions.sh' "$profile" 2>/dev/null; then
echo '' >> "$profile"
echo "$comment" >> "$profile"
echo "$source_line" >> "$profile"
log_success "Added 'source ~/functions.sh' to $profile"
fi
done
log_info "Run 'source ~/.bashrc' or log out and back in to activate. Then run 'db2_help' to see available helper functions."
echo "" >&2
echo " ============================" >&2
echo " source ~/.bashrc" >&2
echo " db2_help" >&2
echo " ============================" >&2
echo "" >&2
}
main
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
Security
What our scanner noticed· Medium risk
- Instructions that use the network
- Downloads piped into a shell
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- AWS
- Publisher
- AWS (Official)
- Source
- aws/agent-toolkit-for-aws · skills/specialized-skills/database-skills/rds-db2 · commit 0a218f8
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by AWS or AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download rds-db2.zip