AWS Builder Center

Building CreditCLI with Kiro: from an AWS credit workflow to a safer cross-platform CLI

Learn how I built CreditCLI with Kiro to manage AWS promotional credits from the terminal—checking balances and expiration dates, exporting data, and registering codes through a guarded workflow. The article covers the GetCredits and RedeemCredits APIs, Boto3 and AWS Login authentication, secret-safe input, dry-run and confirmation gates, property-based testing, and Windows, macOS, and Linux packaging.

AWS Ambassador, AWS Authorized Instructor Champion, AWS Community Builder

Hello, Kiro Community!

I built CreditCLI with Kiro so that I could manage and operate AWS promotional credits without relying on the AWS Management Console for routine checks.
AWS promotional credits are useful, but I found them difficult to manage as an ongoing workflow. To keep checking balances and expiration dates, I had to sign in to the AWS Management Console regularly, which became tedious. The AWS CLI commands available in my environment did not provide this workflow, so I wanted a practical way to handle it from my terminal.
I also wanted to handle one related task in the same place. If AWS provided an official way to register a promotional credit code, I wanted the tool to support that workflow without making a state-changing operation feel as casual as a balance check.
Those needs became CreditCLI, an open-source command-line tool for AWS promotional credits. It provides  balance ,  expiring ,  export , and  redeem  commands, runs from Python source, and is packaged for Windows, macOS, and Linux. The source is available at  https://github.com/hiro-sys/aws-credit-funcitons .

The three things I asked Kiro to help me achieve

I started with three outcomes, not API names or an implementation plan:
  1. I wanted to manage AWS promotional credits by checking balances and expiration dates.
  1. If AWS had an official method, I wanted to register a promotional credit code from the same tool.
  1. I wanted to export the data to CSV or another structured format for administration and follow-up processing.
I asked Kiro to investigate whether AWS provided official APIs for these workflows. By searching the official AWS API Reference, we found  GetCredits  and  RedeemCredits . That discovery gave the project a clear boundary:  GetCredits  could support inspection, expiration checks, and CSV or JSON export, while  RedeemCredits  could support registration behind a deliberate safety gate.
From there, I worked with Kiro to turn those outcomes into requirements and design decisions. We then expanded the work into Boto3 integration, authentication, safe input, testing, and distribution for three operating systems. I did not hand the entire project over to Kiro. Instead, I used Kiro as a development partner that helped carry my product and security decisions consistently through the code, tests, and release workflow.

What the official Billing APIs made possible

The   GetCredits  API  accepts a 12-digit  accountId  and requires a  startDate  in the past, no more than one year before the current time. CreditCLI uses that response to build its balance, expiration, and export workflows.
The  payerAccountFlag  option also matters for consolidated billing. When it is  true  and the caller is the management account, AWS can return credits for the consolidated billing family. CreditCLI enables that behavior by default and provides  --no-payer-account-flag  when I want to query only the specified account.
The   RedeemCredits  API  accepts a promotional credit code from 1 through 256 characters. A successful request returns HTTP 200 and registers the credit in the authenticated calling account's credit ledger. Unlike  GetCredits , the request does not contain a target Account ID; the authenticated calling account determines where the credit is registered. That difference directly influenced the preflight checks and Account ID warnings described later.

The CLI I built

CreditCLI keeps the read-oriented commands small and composable:
Command
Purpose
 balance 
Show active credits, counts, and totals by currency. This is also the default when no subcommand is given.
 expiring 
Find active credits expiring by the selected UTC cutoff.
 export 
Write every credit returned by  GetCredits  as a table, JSON, or CSV.
 redeem 
Register promotional credit codes only after input validation, preflight, and an explicit safety decision.
Here are representative examples of all four commands:
./creditcli balance --profile EXAMPLE_PROFILE_NOT_REAL
./creditcli expiring --expiry-basis end-of-month --profile EXAMPLE_PROFILE_NOT_REAL
./creditcli export --format csv --output EXAMPLE_CREDITS_NOT_REAL.csv --profile EXAMPLE_PROFILE_NOT_REAL
./creditcli redeem --prompt-code --dry-run --profile EXAMPLE_PROFILE_NOT_REAL
 balance  shows credits that are enabled, still have a positive remaining amount, and have not expired. Running CreditCLI without a subcommand also invokes  balance .  expiring  can use either the end of the current UTC month or the first instant of the next UTC month as its inclusive boundary.  export  writes all credits returned by  GetCredits  in table, JSON, or CSV format. The results can also be filtered by Account ID, credit type, or maximum remaining amount and sorted by expiration or remaining amount.
The  --help  output remains Japanese, even when English result output is available. The following terminal output is the current top-level help; it includes the four subcommands and  --version :
$ ./creditcli --help
usage: creditcli [-h] [--version] {balance,expiring,redeem,export} ...

AWS プロモーショナルクレジットの残高照会・有効期限確認・登録・出力を行うクロスプラットフォーム CLI ツール

positional arguments:
{balance,expiring,redeem,export}
実行するサブコマンド(未指定時は balance として扱われる)
balance 現在有効なクレジット残高を表示する(既定動作)
expiring 基準日までに有効期限を迎えるクレジットを表示する
redeem promoCode を登録する
export クレジット情報を table/CSV/JSON 形式で出力する

options:
-h, --help show this help message and exit
--version バージョンを表示して終了する
Japanese is the default for user-facing results from  balance ,  expiring ,  export , and  redeem . The  --lang en  option changes result labels, summaries, notifications, and errors to English. It does not translate command help, so  --help  remains Japanese.
Here is one sanitized English balance example. All IDs and identifying description text are masked, and the seven remaining amounts add up to the displayed USD total.
$ ./creditcli balance --lang en
Credit ID | Description | Initial Amount | Remaining Amount | Currency | Start Date | End Date | Status | Account ID
----------+--------------------------+----------------+------------------+----------+----------------------+----------------------+---------+-------------
****-01 | **** promotional credit | 500.000000 | 500.000000 | USD | 2026-01-01T00:00:00Z | 2026-12-31T23:59:59Z | ENABLED | ************
****-02 | **** promotional credit | 500.000000 | 500.000000 | USD | 2026-01-01T00:00:00Z | 2026-12-31T23:59:59Z | ENABLED | ************
****-03 | **** promotional credit | 500.000000 | 500.000000 | USD | 2026-01-01T00:00:00Z | 2026-12-31T23:59:59Z | ENABLED | ************
****-04 | **** promotional credit | 250.000000 | 250.000000 | USD | 2026-02-01T00:00:00Z | 2027-01-31T23:59:59Z | ENABLED | ************
****-05 | **** promotional credit | 250.000000 | 250.000000 | USD | 2026-02-01T00:00:00Z | 2027-01-31T23:59:59Z | ENABLED | ************
****-06 | **** promotional credit | 200.000000 | 200.000000 | USD | 2026-03-01T00:00:00Z | 2027-02-28T23:59:59Z | ENABLED | ************
****-07 | **** promotional credit | 100.000000 | 50.010000 | USD | 2026-03-01T00:00:00Z | 2027-02-28T23:59:59Z | ENABLED | ************

Matching credits: 7
Totals by currency:
USD: 2,250.010000
The same language option applies to  expiring  and  export  user-facing output as well as redemption messages. The help itself remains Japanese by design.
For completeness, this is a synthetic and sanitized example of a successful registration. The Account ID is non-real, the hidden value is never shown, and no real code, fragment, or hash appears in the transcript.
$ ./creditcli redeem --prompt-code --lang en
Enter a promoCode (input hidden; leave empty to finish): [hidden input]
Enter a promoCode (input hidden; leave empty to finish): [empty input]
Target AWS Account ID: 000000000000
Redemption target count: 1
Proceed with redemption? [y/N]: y
Successful redemptions: 1
Failed redemptions: 0

Architecture and authentication

The implementation separates command behavior from AWS authentication and the Billing transport:

CreditCLI uses the standard Boto3 credential provider chain. If I omit  --account-id , it calls STS  GetCallerIdentity  in  us-east-1  to resolve the calling Account ID. The relevant IAM actions are  billing:GetCredits ,  billing:RedeemCredits , and, when caller identity must be resolved,  sts:GetCallerIdentity .
I also wanted the packaged CLI to work with  AWS Login credentials . After  aws login  obtains short-term credentials, Boto3 can use the resulting profile through its Login credentials provider. That provider requires AWS Common Runtime, so the runtime dependencies are pinned to these versions:
  •  boto3==1.43.42 
  •  botocore[crt]==1.43.42 
  •  awscrt==0.32.2 
For Billing calls, CreditCLI prefers the native Boto3  get_credits()  and  redeem_credits()  operations available in the pinned environment. It retains a SigV4-signed HTTP fallback for environments whose service model does not expose both operations. The fallback is a compatibility boundary, not the source of the API design; the official AWS API Reference remains the contract I used to shape the workflows.

Making promotional credit code registration deliberately harder to trigger

Reading a balance and registering a credit have different risk profiles. A failed read can be repeated, but an uncertain state-changing request may already have succeeded. I therefore designed  redeem  as a guarded workflow rather than another direct subcommand call.
An unredeemed promotional credit code should be treated as a secret. Putting it directly in a positional command argument can expose it through shell history or a process listing. A plaintext file can also be copied into backups, synchronization services, editor history, or version control. CreditCLI therefore recommends these two input modes instead:
./creditcli redeem --prompt-code --dry-run --profile EXAMPLE_PROFILE_NOT_REAL
SECRET_MANAGER_COMMAND_NOT_REAL | ./creditcli redeem --from-stdin --dry-run --profile EXAMPLE_PROFILE_NOT_REAL
 --prompt-code  reads hidden input from a TTY for a person at a terminal.  --from-stdin  reads one UTF-8 code per line for approved automation or a secret provider and is always treated as non-interactive. The positional and  --from-file  modes remain for compatibility, but safe and legacy input modes cannot be mixed.
Before any call to  RedeemCredits , CreditCLI trims surrounding whitespace, drops empty entries, keeps only the first occurrence of a duplicate, and checks the 1-to-256-character limit. It then shows only the resolved Account ID and the number of valid unique targets. Individual results use one-based input numbers instead of a promotional credit code, fragment, or derived hash.
The execution gate follows a small set of rules:
  •  --dry-run  never constructs the Billing client or calls  RedeemCredits .
  • An interactive run proceeds only when the response is  y  or  yes , ignoring case and surrounding whitespace.
  • Any other response or EOF cancels safely with exit code 0.
  • A non-interactive real run requires  --yes .
  • If  --yes  and  --dry-run  are supplied together, the dry run wins.
A dry run still resolves Boto3 credentials. If  --account-id  is omitted, it also calls STS, so it is not necessarily offline. It does not validate  billing:RedeemCredits  permission, determine whether a promotional credit code is registered or unused, verify that the code is valid, or predict whether AWS will accept it.
Here is a synthetic dry-run transcript using a non-real Account ID. The input is hidden and then terminated with an empty entry; the value itself is not shown.
$ ./creditcli redeem --prompt-code --dry-run --lang en
Enter a promoCode (input hidden; leave empty to finish): [hidden input]
Enter a promoCode (input hidden; leave empty to finish): [empty input]
Target AWS Account ID: 000000000000
Redemption target count: 1
Static executability: executable (based only on input validation and Account ID resolution; AWS permissions and code usability are not checked)
Dry run: no redemption was performed.
This dry run is only a synthetic example of static input and Account ID checks. It does not confirm AWS permissions, whether the code is registered or unused, or whether the code is valid and acceptable to AWS.
There is also an Account ID boundary to keep in mind.  RedeemCredits  always applies to the authenticated calling account. An explicitly supplied  --account-id  is checked for a 12-digit format but is not independently compared with the caller, so omitting it provides the stronger identity check through STS before a real registration.
Finally, the retry policies differ by operation.  GetCredits  retries bounded transient failures, but  RedeemCredits  retries only throttling. CreditCLI does not automatically repeat a network error, timeout, or HTTP 5xx for registration because the first request may already have changed the account.

Testing without real credentials or credits

The current suite has 747 passing tests across unit, integration, smoke, regression, and property-based layers. Each safety-focused Property-Based Test runs 200 generated examples. These properties cover input normalization, allowed input-mode combinations, confirmation decisions, API non-invocation, output independence from the contents of promotional credit codes, sanitization, ordered registration, and retry behavior.
The automated tests do not depend on real AWS credentials or a real promotional credit code. They replace inherited AWS configuration with isolated synthetic settings, disable instance metadata, direct SDK endpoints to loopback, and block socket connections. Synthetic clients and values exercise authentication initialization, API selection, preflight, confirmation, sanitization, and failure handling without sending requests to AWS public endpoints.
The public GitHub Actions workflow validates the release on all three supported operating systems:
Runner
Result
 windows-latest 
Passed
 macos-latest 
Passed
 ubuntu-latest 
Passed
All three operating systems passed 747 tests, the PyInstaller distribution build, package smoke checks, and archive validation. The public workflow results are available in  GitHub Actions  . 
PyInstaller cannot cross-compile these distributions, so GitHub Actions builds each operating system independently. Every job verifies the pinned dependencies and runs the full suite. It then builds a PyInstaller  onedir  distribution, a format that places the executable and its required libraries in one directory. The job also checks  _awscrt  and the required botocore service models, then performs a synthetic AWS Login initialization smoke test before publishing an artifact.
I chose  onedir  because the executable must stay beside its Python modules, data, service models, and native libraries. The GitHub Actions download contains an outer artifact ZIP and an inner distribution ZIP, so extraction takes two steps.
The macOS artifact is not Developer ID signed or notarized. The Linux artifact is built on  ubuntu-latest ; AWS CloudShell has reported  GLIBC_2.38 not found , so the current binary requires glibc 2.38 or later, source execution, or a rebuild for the target environment. The Windows artifact and successful CI jobs also do not imply compatibility with every possible machine. These packaging constraints are operational details rather than a claim of universal compatibility.

Trying CreditCLI

Python 3.10 or later is required when running from source:
git clone https://github.com/hiro-sys/aws-credit-funcitons.git
cd aws-credit-funcitons
python3 -m venv .venv
source .venv/bin/activate
python -m pip install .
I recommend starting with AWS Login and a read operation:
aws login --profile EXAMPLE_PROFILE_NOT_REAL
creditcli --help
creditcli balance --profile EXAMPLE_PROFILE_NOT_REAL
creditcli expiring --expiry-basis end-of-month --profile EXAMPLE_PROFILE_NOT_REAL
creditcli export --format csv --output EXAMPLE_CREDITS_NOT_REAL.csv --profile EXAMPLE_PROFILE_NOT_REAL
For registration, I first review a dry run with hidden input:
creditcli redeem --prompt-code --dry-run --profile EXAMPLE_PROFILE_NOT_REAL
Before removing  --dry-run , I check the resolved Account ID, target count, IAM permissions, and the complete safety guidance in the repository. I never place a real promotional credit code in an article command, screenshot command, issue, or log.

What I learned

Starting from the workflow kept the project focused. I could ask Kiro to investigate the official AWS surface before committing to a transport or SDK method, and the  GetCredits  and  RedeemCredits  contracts then shaped separate read and state-changing paths.
I also learned that a useful CLI is more than an API wrapper. For  redeem , the design work was in the boundaries around the request: secret input, normalization, caller identity, preflight, explicit approval, conservative retries, and sanitized results. For distribution, support for AWS Login was not complete until the CRT dependency and native extension were present and tested in each packaged operating system.
Kiro was most useful as a development partner for carrying those decisions across requirements, implementation, tests, and release checks. The result still reflects choices I made about what the tool should do and what it should refuse to do automatically.

Limitations, license, and official references

CreditCLI is an independent, unofficial open-source project and is not an AWS service or endorsed by AWS. It is distributed under the MIT License without warranty. Before using it operationally, review the source and verify IAM permissions, the authenticated account, dependency compatibility, and network behavior in the target environment.
The current limitations include the unsigned and unnotarized macOS artifact, the Linux glibc requirement, Japanese-only command help, and the limited scope of dry-run described above.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article