DevOps Practitioner · Module 4: Infrastructure as Code
Modules With Clean Inputs and Outputs
Modules turn repeated infrastructure into a named thing with a contract. Inputs declare what the caller must decide, outputs declare what the module promises back, and everything else stays inside.
10 min reading
Objectives
- Explain what a module boundary hides and what it exposes
- Design inputs with types, defaults and validation
- Expose only the outputs callers actually need
- Version and pin module sources instead of tracking moving heads
Why this matters
Five services each declare their own storage bucket with slightly different settings: encryption here, versioning there, logging nowhere. A compliance review fails on the two nobody remembered. A module replaces the five copies with one reviewed definition: encryption always on, versioning defaulted, logging wired, inputs for the name and the few real choices. The next service gets the compliant shape by default because the default is the only easy path. Consistency stops depending on memory.
Concepts
A module is a directory of configuration with a contract. Inputs (variables) carry types, descriptions, defaults for the genuinely optional, and validation rules that reject nonsense at plan time rather than at 2 AM. No validation means every caller invents its own wrong value; the module author knows the constraints, so the module enforces them. Outputs expose what callers need (bucket name, endpoint, IDs) and hide the rest; an output for every internal is not a contract, it is the absence of one.
Composition beats nesting depth. Root configurations call modules and wire outputs to inputs; modules calling modules is fine one level down, beyond that the dependency graph needs a diagram and the diagram is a warning. Keep modules small and single-purpose: a networking module, a storage module, a service module, each testable with local fixtures.
Sources must be pinned. Registry versions, git tags, archive hashes: any reference that can move will move, at the worst time. The lock file records the resolved versions; review its changes like code changes, because they are. A module update is a deliberate act with its own plan, never a surprise inside an unrelated apply.
Worked example
A demo storage module wraps a local fixture resource with validated inputs (name pattern, retention choices) and two outputs. The learner calls it twice with different inputs, passes an invalid name to watch validation reject it at plan time, and reads both outputs wired into a second module. The invalid plan is kept as proof that the contract bites.
Common wrong move
Building one mega-module that provisions the whole environment behind six flags. It cannot be tested, cannot be reused and cannot be reviewed; every change risks everything. Split by blast radius and lifecycle, not by enthusiasm.
Quick check
An optional 4-question self-check. Answers never leave your device, are not stored, and never count toward any assessment.
Lesson feedback
No published feedback yet.
Log in and complete the lesson to leave feedback.
Exercise
Write a small fixture module with typed, validated inputs and minimal outputs, call it twice, and quote the plan-time rejection of an invalid input.
Pass criteria
The record shows the module contract, two working calls with distinct inputs, and the quoted validation rejection.