TT Lab
Get started
Learn Learning paths Courses

Infrastructure as Code

An Idempotent Resource-Assurance Script

Continue in TT Lab

Goal

Build idempotent scripts that ensure directories, files, lines, and permissions, and prove idempotency by applying twice in a row and getting changed of 0. Also add a state file, drift detection, and a lock.

Why it matters

Idempotency is the property that running the same operation several times gives the same result, and it is the assurance that automation is safe to rerun. The way to prove it is to apply twice and check that the second plan shows no changes. The places where idempotency breaks are nearly fixed. They are where commands are run directly with shell or command instead of a dedicated module, and where lines are appended with >>. So the scripts in this lab all share the same skeleton of "check before changing." The state file and lock added at the end let you understand firsthand why real IaC tools carry such accessories.

Steps

  1. Create /root/iac2/ensure_dir.sh <경로> (the placeholder is the path). If the directory does not exist, create it and print changed; if it already exists, print unchanged. In both cases the exit code is 0. The output must be one line containing only that word (grading compares unchanged against the whole line exactly).
  2. Create /root/iac2/ensure_file.sh <경로> <내용> (the placeholders are the path and the content). If the file does not exist or its content differs, write the content and print changed; if it is already the same, print unchanged.
  3. Create /root/iac2/ensure_line.sh <파일> "<줄>" (the placeholders are the file and the line). If that line already exists exactly, print unchanged; if another line with the same key (the first word of the line) exists, replace that line in place with the new line and print changed; if there is nothing, append it at the end and print changed. However many times it runs, that line must appear exactly once in the file, and the other lines must remain as they are.
  4. Create /root/iac2/ensure_mode.sh <파일> <8진수모드> (the placeholders are the file and the octal mode). Read the current permission with stat -c %a; if it differs, run chmod and print changed; if it is the same, print unchanged. If the file does not exist, exit with a non-zero exit code.
  5. Create /root/iac2/run.sh. Using the four scripts above, ensure exactly 4 resources (for example, 1 directory + 2 files + 1 permission). On the last line, print the summary in the order unchanged=<수> changed=<수> (the placeholders are counts). Save the output of the first run to /root/iac2/run1.txt, and then the output of the second run to /root/iac2/run2.txt. The changed of the first run must be 1 or more, and the summary of the second run must be unchanged=4 changed=0.
  6. Create /root/iac2/state.json. In the resources array, record the resources you just ensured (2 or more), and put path, type (file or dir), and sha256 in each element. For items whose type is not dir, the actual file must exist and the sha256 value must be exactly equal to the first field of sha256sum <경로> (the placeholder is the path). Also put applied_at (the time of application) at the top level.
  7. Create /root/iac2/drift.sh <상태파일> (the placeholder is the state file). It reads the state file given as an argument and verifies the recorded resources. If everything matches, exit code 0; if the content changed or a file has disappeared, it prints the mismatched path and returns a non-zero code. For items whose type is dir, it checks only existence.
  8. Create /root/iac2/apply-lock.sh. The lock file path comes from the LOCK_FILE environment variable, and if it is not set, use /root/iac2/.lock. If there is no lock, take it by creating it conditionally, do the work, and always remove it on normal exit (exit code 0, and the lock file must not remain). If it is already locked, do not wait but fail immediately with exit code 2 and print a message containing the word lock. In that case you must not delete someone else's lock file.

Notes

Ensure a directory

Create /root/iac2/ensure_dir.sh <경로> (the placeholder is the path). If the directory does not exist, create it and print changed; if it already exists, print unchanged. In both cases the exit code is 0. The output must be one line containing only that word (grading compares unchanged against the whole line exactly).

ensure_dir.sh <경로> creates it and prints changed if it does not exist, and prints unchanged if it already exists (the placeholder is the path). Both have exit code 0. The output must be one line containing only that one word. Grading compares unchanged against the whole line exactly.

Ensure file contents

Create /root/iac2/ensure_file.sh <경로> <내용> (the placeholders are the path and the content). If the file does not exist or its content differs, write the content and print changed; if it is already the same, print unchanged.

It is ensure_file.sh <경로> <내용> (the placeholders are the path and the content). Compare with the current content before writing. If it is missing or different, write it and print changed; if it is the same, leave it alone and print unchanged. If you write unconditionally, it is always changed, and the proof of idempotency breaks.

Ensure a line (without duplicates)

Create /root/iac2/ensure_line.sh <파일> "<줄>" (the placeholders are the file and the line). If that line already exists exactly, print unchanged; if another line with the same key (the first word of the line) exists, replace that line in place with the new line and print changed; if there is nothing, append it at the end and print changed. However many times it runs, that line must appear exactly once in the file, and the other lines must remain as they are.

It is ensure_line.sh <파일> "<줄>" (the placeholders are the file and the line). If you append with >>, lines pile up every time it runs. If the same line already exists, unchanged; if another line with the same key (first word) exists, replace that line in place and print changed; if there is none, append at the end and print changed. The other lines must remain as they are.

Ensure permissions

Create /root/iac2/ensure_mode.sh <파일> <8진수모드> (the placeholders are the file and the octal mode). Read the current permission with stat -c %a; if it differs, run chmod and print changed; if it is the same, print unchanged. If the file does not exist, exit with a non-zero exit code.

ensure_mode.sh <파일> <8진수모드> reads the current permission with stat -c %a and compares (the placeholders are the file and the octal mode). If it differs, chmod and then changed; if it is the same, unchanged. You must not try chmod on a nonexistent file and report success, so check existence first.

Prove idempotency by running twice

Create /root/iac2/run.sh. Using the four scripts above, ensure exactly 4 resources (for example, 1 directory + 2 files + 1 permission). On the last line, print the summary in the order unchanged=<수> changed=<수> (the placeholders are counts). Save the output of the first run to /root/iac2/run1.txt, and then the output of the second run to /root/iac2/run2.txt. The changed of the first run must be 1 or more, and the summary of the second run must be unchanged=4 changed=0.

With run.sh, ensure exactly 4 resources, and print the summary on the last line in the order unchanged=<수> changed=<수> (the placeholders are counts). The order matters. Save the first run's output to /root/iac2/run1.txt, and then the second run's output to /root/iac2/run2.txt.

Leave a state file

Create /root/iac2/state.json. In the resources array, record the resources you just ensured (2 or more), and put path, type (file or dir), and sha256 in each element. For items whose type is not dir, the actual file must exist and the sha256 value must be exactly equal to the first field of sha256sum <경로> (the placeholder is the path). Also put applied_at (the time of application) at the top level.

Record the resources you just applied in /root/iac2/state.json. Each element has path, type, and sha256, and the hash of a file item must be exactly equal to the first field of sha256sum <경로> (the placeholder is the path). Also put applied_at at the top level. Create it after running run.sh so that the hashes match.

Drift detection

Create /root/iac2/drift.sh <상태파일> (the placeholder is the state file). It reads the state file given as an argument and verifies the recorded resources. If everything matches, exit code 0; if the content changed or a file has disappeared, it prints the mismatched path and returns a non-zero code. For items whose type is dir, it checks only existence.

drift.sh <상태파일> reads the state file given as an argument (the placeholder is the state file). If you hardcode the path, it cannot read the temporary state file that grading creates. It must catch both content changes and file deletions, and it must print the mismatched path so that a person knows what to look at.

State lock

Create /root/iac2/apply-lock.sh. The lock file path comes from the LOCK_FILE environment variable, and if it is not set, use /root/iac2/.lock. If there is no lock, take it by creating it conditionally, do the work, and always remove it on normal exit (exit code 0, and the lock file must not remain). If it is already locked, do not wait but fail immediately with exit code 2 and print a message containing the word lock. In that case you must not delete someone else's lock file.

apply-lock.sh respects the LOCK_FILE environment variable. If there is no lock, create it conditionally and always remove it on normal exit (0). If it is already locked, do not wait but fail immediately with exit code 2 and say in the message that it is because of the lock. Never delete someone else's lock file.