Scripting for security: PowerShell, Bash and Python

Objective 3.1 · Security engineering · 31% of the exam

Why this matters

Domain 3 is 31% of CAS-005 — the largest domain — and CompTIA describe it in five bullets. Automation is the first of them, and four lessons carry it here, which is the arithmetic lesson the course introduction made: one bullet in this domain can be worth more than five in domain 1.

This first lesson is about scripting, and the examinable skill is narrower and more useful than "can you write code". It is can you read a script well enough to approve it, because at this level you are reviewing and authorising automation that will act on production, not writing all of it yourself. A script that runs with administrative rights across the estate is a control with the blast radius of a configuration management system and frequently none of the governance.

The second theme is that automation changes the shape of risk rather than reducing it. A manual process performed badly affects one system at a time. An automated process performed badly affects everything, instantly, and usually before anyone notices — which is why the sections on failing closed, dry runs and logging carry more weight here than syntax ever will.

The lesson

Choosing the language by where the work runs, not by preference

The three languages CompTIA name have genuinely different centres of gravity, and choosing by familiarity rather than by fit is a recognisable mistake.

PowerShell is the right tool inside a Windows-centric estate. Its advantage is not syntax but the object pipeline: commands pass structured objects rather than text, so filtering on a property is reliable rather than a parsing exercise. It has first-class access to Windows management interfaces, the directory service, and most Microsoft cloud and endpoint management APIs, and it runs cross-platform in its modern form. Its security-relevant feature set is unusually strong — script block logging, transcription, constrained language mode and execution policy — and those are examinable as controls rather than as conveniences.

Bash is the right tool for Unix-like hosts and for orchestrating other programs. It excels at gluing command-line tools together and is present on essentially every Linux system without installation, which matters when the script must run somewhere you do not control. Its weakness is that everything is text, so parsing is fragile, and its quoting and word-splitting rules produce defects that are invisible on the happy path — an unquoted variable containing a space is the classic, and it has caused real outages.

Python is the right tool for anything with logic, structure or an API. It handles JSON natively, has libraries for every security platform, is testable, and is readable by people who did not write it — which matters more than elegance for code that will be maintained by a team. Its cost is a runtime and a dependency set to manage, and those dependencies are supply chain surface, as lesson fifteen noted.

The decision rule: where does the work run, and what is it talking to? Windows endpoints and Microsoft services, PowerShell. Unix hosts and existing command-line tools, Bash. APIs, data transformation and anything with branching logic, Python. A scenario naming the environment is usually naming the answer.

One further consideration that recurs in practice: what is already there. A script requiring an interpreter installation on every host has an operational dependency, and in constrained environments the language that is already present wins on availability alone.

Reading a script well enough to approve it, which is the examinable skill

Approving automation is a review task, and it has a checklist. Applied to a script that will run with privilege, these are the questions in order of how much damage a wrong answer causes.

  • What identity does it run as, and what can that identity reach? This bounds everything else. A script running as a broadly privileged account is a broadly privileged action however careful its logic.
  • What does it modify? Enumerate the write operations. Read-only automation is a different risk class and can be approved far more cheaply, which is an argument for splitting scripts that do both.
  • How does it select its targets? A filter that silently matches everything when a variable is empty is the single most common catastrophic defect — the script intended for one group runs against the estate.
  • What happens when something fails partway? Does it stop, continue, or leave systems in a half-changed state? Partial application is often worse than no application.
  • Where do its credentials come from? Covered below, and a plaintext credential is a stop-the-review finding.
  • What does it log, and where? If the script's actions cannot be reconstructed afterwards, its effects cannot be investigated.
  • Can it be reversed? And is the reversal tested, or theoretical?
  • What are its inputs, and are they trusted? A script taking a hostname from a ticket and interpolating it into a command has an injection vulnerability of exactly the kind domain 4 covers.

The review posture worth adopting: treat a script that writes to production as a change of the same weight as the change it performs. If deploying a firewall rule needs approval, a script that deploys two hundred needs more, not less — and the common organisational failure is that the script route bypasses the process the manual route requires.

Handling credentials in scripts: what never goes in a file, and what replaces it

The rule is short: no credential is ever written into a script, a configuration file next to it, or a version-controlled repository. The justification is that scripts are copied, shared, committed and logged, and a credential in one is a credential in all of those places permanently — a repository's history retains it after the file is edited, which is why "we removed it" is not remediation and rotation is.

What replaces it, in decreasing order of strength:

  1. No credential at all. Workload identity, managed identity, or an instance role: the platform grants the script an identity based on where it runs, and issues short-lived tokens automatically. Nothing to steal, nothing to rotate, and this is the answer wherever the platform supports it.
  2. A secrets manager, retrieved at run time, with the script authenticating by workload identity. The secret exists in memory for the duration and is never at rest outside the vault.
  3. An operating system credential store — the platform's own protected store, bound to a user or machine.
  4. An encrypted credential file whose decryption key is separately protected. Weaker, and honest about being a compromise.
  5. Interactive prompting, which is secure and defeats automation, so it is right only for interactive administrative scripts.

Two further practices that scenarios test. Scope the credential to the script's actual task: automation credentials are routinely granted administrative rights because that made the first test pass, and the resulting identity is more valuable than the process it serves. And never pass a secret as a command-line argument, because arguments are visible to other processes and are captured by command-line logging — including, in a pleasing irony, by the security logging that domain 4 recommends enabling.

Idempotence, dry runs and failing closed when a script touches production

Three properties that separate automation you can trust from automation you hope about.

Idempotence means running the script twice produces the same result as running it once. It is what makes automation safe to re-run after a partial failure, safe to schedule as a continuous enforcement mechanism, and safe to invoke when you are unsure whether it already ran. Achieving it means checking state before acting — if the rule is absent, add it rather than add the rule — and it is the property that converts a deployment script into a control that continuously corrects drift, as lesson fourteen said of configuration management.

Dry runs. A script that changes production must be able to report what it would change without changing it, and the review should include reading that output on the real target set. This is where the empty-filter defect is caught: a dry run reporting 4,000 systems when you expected 12 is the cheapest possible discovery of that bug. Making dry run the default, with a flag required to apply, is the design that fails in the safe direction.

Failing closed. When something unexpected happens, the script stops rather than continuing on assumptions. Concretely: abort on an error rather than carrying on to the next item; abort on an argument or option you do not recognise, because an ignored flag means the operator asked for something the script did not do; refuse to run against an empty or implausibly large target set; and require an explicit confirmation for destructive operations.

That middle point deserves emphasis because it is unglamorous and costly when missed. A mutating script that silently ignores an unknown flag will, sooner or later, perform the default action on a large production set while its operator believes they asked for something narrower. The failure is silent, immediate and wide.

Rate limiting and batching complete the picture. A script that changes ten systems, pauses, and checks health before continuing turns a catastrophic defect into a small one. Blast radius is a design parameter of the script itself, not only of the credential it holds.

Logging and error handling so an automated action can be reconstructed afterwards

The last property, and the one that determines whether an incident involving automation can be investigated at all.

What a script that acts on production should record, per run and per target:

  • When it ran, as what identity, from where, and who or what invoked it.
  • Its parameters, with secrets redacted — including the target selection, because "which systems" is the first question afterwards.
  • What it found before acting, which is what makes a change explicable later.
  • What it changed, per target, with before and after values where practical.
  • What failed, with the actual error rather than a generic message.
  • The outcome, including the count of targets attempted, succeeded and failed. A run that reports success while having skipped half its targets is the failure mode this count exists to catch.

Two design points. Logs should go somewhere central and outside the script's own control, for the same reason lesson twenty-three put a zone's logs outside the zone: a script running as an administrator can alter local logs. And error messages should name the failure, not the last thing that happened — a runner that reports the final line of a crashed child's output will faithfully report the interpreter's version banner for every distinct fault, which is a real pattern and hides twenty different problems behind one identical string.

The connective point into the rest of domain 3: automation is a control, and every control in this course is subject to the same demand — that it can be shown to work, and shown to fail when it should. A script nobody has watched refuse a bad input is a script nobody has evidence about, which is the same argument lesson twenty-three made about proving a perimeter and the same one the next three lessons will make about playbooks, pipelines and patching.

Practise what you just read

1. What is the examinable scripting skill at this level?

Select one

  1. Reading a script well enough to approve it
  2. Converting an existing manual runbook into an executable form that produces the same result without operator intervention
  3. Writing a script that performs a security task correctly
  4. Choosing the fastest implementation for a large estate
Show answer

A. At this level you authorise automation that acts on production rather than writing all of it. A script running with administrative rights across the estate is a control with the blast radius of a configuration management system.

2. Which question should come first when reviewing a mutating script?

Select one

  1. What does it log
  2. What identity does it run as, and what can that identity reach
  3. Which language is it written in, and does the team maintaining it have the skills to support that choice over the life of the script
  4. Is it idempotent
Show answer

B. That bounds everything else. A script running as a broadly privileged account is a broadly privileged action however careful its logic, and no amount of defensive coding narrows what the credential can reach.

3. Which defect is the single most common catastrophic one in a target-selecting script?

Select one

  1. An unhandled exception halting midway
  2. A hard-coded hostname
  3. A filter that silently matches everything when a variable is empty
  4. An off-by-one error in the batching logic that causes the final group of targets to be skipped on every run
Show answer

C. The script intended for one group runs against the estate. A dry run reporting four thousand systems when you expected twelve is the cheapest possible discovery of that bug.

10 more questions on this objective are part of the full course.

Practise the full question bank in the exam simulator

Hands-on labs

All hands-on labs

This is an independent study companion for CompTIA SecurityX CAS-005 and is not produced by or endorsed by CompTIA.