Learn Python core architecture, production scenario-based questions, incident response, and real-world engineering solutions.
❓ Q1: What is the difference between a List and a Tuple?
Click on the dropdown below to reveal the technical answer.
Answer:
[]): Mutable (can be modified after creation).()): Immutable (cannot be changed once created).(200, 201, 204)).my_list = [1, 2, 3]
my_tuple = (1, 2, 3)
my_list[0] = 10 # Works
# my_tuple[0] = 10 # Raises TypeError
❓ Q2: What is the difference between the
==andisoperators?Click on the dropdown below to reveal the technical answer.
Answer:
==: Checks for value equality (if the elements inside are identical).is: Checks for identity reference (if both variables point to the exact same object in memory).a = [1, 2]
b = [1, 2]
print(a == b) # True (same values)
print(a is b) # False (different locations in memory)
❓ Q3: What are the primary Python data types?
Click on the dropdown below to reveal the technical answer.
Answer:
str): Character sequences (e.g., IP addresses, log logs).int): Whole numbers (e.g., ports, retry counts).float): Decimals (e.g., system load average).bool): True or False.list): Ordered, mutable sequence of items.tuple): Ordered, immutable sequence of items.dict): Key-value mappings (highly used for JSON configurations).set): Unordered collection of unique items (useful for deduplicating data).❓ Q4: What is a dictionary and why is it heavily used in DevOps?
Click on the dropdown below to reveal the technical answer.
Answer: A Dictionary stores data in key-value pairs. Since most cloud infrastructure APIs (AWS, GCP, Kubernetes) return responses in JSON format, Python dictionaries are the native structure used to parse, manipulate, and generate configuration parameters.
server = {
"name": "web-server",
"ip": "10.0.0.1"
}
print(server["ip"])
❓ Q5: How do you print all EC2 instance names stored in a list?
Click on the dropdown below to reveal the technical answer.
Answer:
You can iterate through the list using a standard for loop:
instances = ["web", "app", "db"]
for instance in instances:
print(instance)
❓ Q6: What is the difference between a
forloop and awhileloop?Click on the dropdown below to reveal the technical answer.
Answer:
for: Iterates over a defined sequence (list, range, dictionary keys).while: Loops as long as a boolean condition remains True.# For Loop
for i in range(5):
print(i)
# While Loop (waiting for something to finish)
while True:
# Do health check
break
❓ Q7: Why do we use functions?
Click on the dropdown below to reveal the technical answer.
Answer: Functions are used to enforce DRY (Don't Repeat Yourself) principles. They bundle blocks of code to make scripts modular, testable, and reusable.
def backup():
print("Backup started")
backup() # Reusable call
❓ Q8: What is the difference between a parameter and an argument?
Click on the dropdown below to reveal the technical answer.
Answer:
def deploy(env): # env is the parameter
pass
deploy("prod") # "prod" is the argument
❓ Q9: How do you read a log file safely in Python?
Click on the dropdown below to reveal the technical answer.
Answer:
Use the with open(...) context manager, which ensures that file descriptors are automatically closed even if the script raises an error.
with open("app.log", "r") as file:
print(file.read())
❓ Q10: Why do we use the
osmodule?Click on the dropdown below to reveal the technical answer.
Answer:
The os module provides standard interfaces to interact with the underlying host OS. It is used for listing directory contents, checking file paths, executing system operations, and loading environment variables.
import os
print(os.getcwd()) # Get current working directory
❓ Q11: What is the difference between
os.path.exists()andos.path.isfile()?Click on the dropdown below to reveal the technical answer.
Answer:
os.path.exists(path): Returns True if either a directory or a file exists at the path.os.path.isfile(path): Returns True only if a regular file exists at that path (returns False if the path points to a directory).import os
os.path.exists("/var/log") # True (it is a directory)
os.path.isfile("/var/log") # False (it is not a file)
❓ Q12: What does
os.listdir()return?Click on the dropdown below to reveal the technical answer.
Answer:
os.listdir(path) returns a list containing the names of the entries (both files and subdirectories) inside the specified directory, excluding . and ...
import os
print(os.listdir("/tmp")) # Output: ['file1.txt', 'cache_dir']
❓ Q13: What is the difference between
os.remove()andos.rmdir()?Click on the dropdown below to reveal the technical answer.
Answer:
os.remove(path): Deletes a file. Raises an error if the path points to a directory.os.rmdir(path): Deletes an empty directory. Raises an error if the directory contains files or subdirectories.❓ Q14: How do you create nested directories (like
mkdir -p) in Python?Click on the dropdown below to reveal the technical answer.
Answer:
Use os.makedirs(path, exist_ok=True). Setting exist_ok=True ensures the script does not raise an exception if the target directories already exist.
import os
os.makedirs("/opt/app/logs/prod", exist_ok=True)
❓ Q15: How do you read environment variables using
os.environandos.getenv()? What is the difference?Click on the dropdown below to reveal the technical answer.
Answer:
os.environ["VAR"]: Directly accesses the environment variable dictionary. If "VAR" is missing, it raises a KeyError and crashes.os.getenv("VAR", default): Safely queries the variable. If missing, it returns None (or the specified default value) without crashing.import os
# Safe check with fallback value
db_port = os.getenv("DB_PORT", "5432")
❓ Q16: What is the difference between
shutil.copy()andshutil.copy2()?Click on the dropdown below to reveal the technical answer.
Answer:
shutil.copy(src, dst): Copies the file contents and permissions. It does not preserve file metadata (like creation and modification times).shutil.copy2(src, dst): Copies the file contents, permissions, and preserves all metadata (timestamps).❓ Q17: What does
shutil.move()do?Click on the dropdown below to reveal the technical answer.
Answer:
shutil.move(src, dst) recursively moves a file or directory to another destination. It functions like the Linux mv command.
❓ Q18: How do you copy an entire directory recursively in Python?
Click on the dropdown below to reveal the technical answer.
Answer:
Use shutil.copytree(src, dst). It copies the entire directory tree from src to a new directory named dst (which must not exist beforehand unless dirs_exist_ok=True is set).
import shutil
shutil.copytree("/var/log/nginx", "/backup/nginx_logs", dirs_exist_ok=True)
❓ Q19: How do you delete a directory recursively in Python?
Click on the dropdown below to reveal the technical answer.
Answer:
Use shutil.rmtree(path). This deletes the directory and all of its contents (files, subdirectories, links) recursively. Use with caution!
import shutil
shutil.rmtree("/tmp/cache_folder")
❓ Q20: How do you check disk usage using
shutil.disk_usage()?Click on the dropdown below to reveal the technical answer.
Answer:
shutil.disk_usage(path) returns a named tuple containing total, used, and free bytes on the given path.
import shutil
total, used, free = shutil.disk_usage("/")
print(f"Used Space: {used / (1024**3):.2f} GB")
❓ Q21: What is the difference between
datetime.now()anddatetime.utcnow()?Click on the dropdown below to reveal the technical answer.
Answer:
datetime.now(): Returns the current local date and time based on the host system's timezone settings.datetime.utcnow(): Returns the current date and time in UTC (Coordinated Universal Time). In cloud/DevOps automation, always use UTC to avoid discrepancies between servers located in different regions.❓ Q22: How do you calculate the difference between two dates in Python?
Click on the dropdown below to reveal the technical answer.
Answer:
Subtracting two datetime objects returns a timedelta object, which represents the duration between them.
from datetime import datetime
date1 = datetime(2026, 6, 1)
date2 = datetime(2026, 6, 5)
diff = date2 - date1
print(diff.days) # Output: 4
❓ Q23: What is the difference between
subprocess.run()andsubprocess.Popen()?Click on the dropdown below to reveal the technical answer.
Answer:
subprocess.run(): Synchronous/blocking. It runs the command, waits for it to complete, and returns a CompletedProcess instance.subprocess.Popen(): Asynchronous/non-blocking. It spawns the command in a background process immediately and allows the Python script to continue running concurrently, interacting with standard streams in real time.❓ Q24: Why is
shell=Truesometimes considered dangerous in scripts?Click on the dropdown below to reveal the technical answer.
Answer:
If the command string includes untrusted user inputs, shell=True exposes the system to shell injection vulnerabilities (where an attacker inserts extra characters like ; rm -rf / to execute arbitrary commands). If possible, pass arguments as a list with shell=False.
❓ Q25: How do you capture standard output and check return codes using
subprocess.run()?Click on the dropdown below to reveal the technical answer.
Answer:
Pass stdout=subprocess.PIPE and stderr=subprocess.PIPE. You can then check the exit status using returncode.
import subprocess
result = subprocess.run("echo 'hello'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print("Exit Status:", result.returncode)
print("Output:", result.stdout.decode().strip())
❓ Q26: What is
sys.argv? How do you pass and parse command-line arguments in scripts?Click on the dropdown below to reveal the technical answer.
Answer:
sys.argv is a list containing the command-line arguments passed to the active Python script.
sys.argv[0]: The name of the script itself.sys.argv[1]: The first argument passed after the script name.# Save as test.py
import sys
print(f"Script Name: {sys.argv[0]}")
if len(sys.argv) > 1:
print(f"First Arg: {sys.argv[1]}")
Running python3 test.py prod will output:
Script Name: test.py
First Arg: prod
❓ Q27: What is
sys.exit()and why is it used in production scripts?Click on the dropdown below to reveal the technical answer.
Answer:
sys.exit(code) raises the SystemExit exception to terminate the script execution. Passing 0 indicates a successful termination, while any non-zero value (typically 1) signals a failure to the calling shell or CI/CD workflow runner.
❓ Q28: How does Boto3 authenticate to AWS?
Click on the dropdown below to reveal the technical answer.
Answer: Boto3 looks for credentials in the following order:
boto3.client() constructor.AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN).~/.aws/credentials and ~/.aws/config).❓ Q29: What is the difference between a Client and a Resource in Boto3?
Click on the dropdown below to reveal the technical answer.
Answer:
bucket.objects.all()). Not all AWS services are supported by resources.❓ Q30: How do you list EC2 instances, filter them by tags, and start/stop them using Boto3?
Click on the dropdown below to reveal the technical answer.
Answer:
Use the filter() method with appropriate criteria:
import boto3
ec2 = boto3.resource("ec2", region_name="us-east-1")
# Filter instances that are stopped and have the tag Name=TestServer
instances = ec2.instances.filter(
Filters=[
{"Name": "instance-state-name", "Values": ["stopped"]},
{"Name": "tag:Name", "Values": ["TestServer"]}
]
)
# Start them
for instance in instances:
print(f"Starting instance: {instance.id}")
instance.start()
❓ Q31: How do you handle pagination when listing hundreds of objects in Boto3 S3 APIs?
Click on the dropdown below to reveal the technical answer.
Answer: AWS APIs paginate responses when resource counts are large. You can handle pagination using a Boto3 Paginated Query:
import boto3
client = boto3.client("s3")
paginator = client.get_paginator("list_objects_v2")
# Create a page iterator
page_iterator = paginator.paginate(Bucket="my-large-s3-bucket")
for page in page_iterator:
if "Contents" in page:
for obj in page["Contents"]:
print(obj["Key"])
❓ Q32: How do you assume an IAM role programmatically inside a Boto3 script?
Click on the dropdown below to reveal the technical answer.
Answer:
Call AWS Security Token Service (STS) assume_role to fetch temporary credentials, then initialize a new Boto3 session:
import boto3
sts = boto3.client("sts")
assumed_role = sts.assume_role(
RoleArn="arn:aws:iam::123456789012:role/TargetDevOpsRole",
RoleSessionName="AssumedRoleSession"
)
# Extract credentials
creds = assumed_role["Credentials"]
# Create session with temporary credentials
session = boto3.Session(
aws_access_key_id=creds["AccessKeyId"],
aws_secret_access_key=creds["SecretAccessKey"],
aws_session_token=creds["SessionToken"]
)
# Use resources within the context of the assumed role
s3 = session.resource("s3")
❓ Scenario 1: Write a script that checks disk usage. If usage is > 90%, compress old logs and upload them to AWS S3.
Click on the dropdown below to reveal the technical answer.
Answer:
import os
import shutil
import boto3
from datetime import datetime
# Configurations
THRESHOLD = 90.0
LOG_DIR = "/var/log/app"
BACKUP_FILE = f"/tmp/logs_backup_{datetime.now().strftime('%Y%m%d')}"
S3_BUCKET = "devops-incident-backups"
# 1. Check disk usage
total, used, free = shutil.disk_usage("/")
percent_used = (used / total) * 100
if percent_used > THRESHOLD:
print(f"Disk alert: {percent_used:.2f}% used! Commencing cleanup...")
# 2. Compress log directory
if os.path.exists(LOG_DIR):
archive_path = shutil.make_archive(BACKUP_FILE, 'gztar', LOG_DIR)
print(f"Logs compressed to: {archive_path}")
# 3. Upload to S3
s3 = boto3.client("s3")
try:
s3.upload_file(archive_path, S3_BUCKET, os.path.basename(archive_path))
print("Compressed archive uploaded to S3 successfully.")
# 4. Cleanup old logs (Optional / Safe truncation)
for file in os.listdir(LOG_DIR):
file_path = os.path.join(LOG_DIR, file)
if os.path.isfile(file_path):
open(file_path, 'w').close() # Truncate content keeping file descriptors open
# Remove temporary archive
os.remove(archive_path)
except Exception as e:
print(f"Backup upload failed: {e}")
❓ Scenario 2: Write a script that lists all EC2 instances, identifies any in a 'stopped' state, and starts them automatically.
Click on the dropdown below to reveal the technical answer.
Answer:
import boto3
ec2 = boto3.resource("ec2", region_name="us-east-1")
# Find stopped instances
stopped_instances = ec2.instances.filter(
Filters=[{"Name": "instance-state-name", "Values": ["stopped"]}]
)
instance_ids = [instance.id for instance in stopped_instances]
if instance_ids:
print(f"Found stopped instances: {instance_ids}. Starting them now...")
# Start the instances
ec2.instances.filter(InstanceIds=instance_ids).start()
else:
print("No stopped instances found.")
❓ Scenario 3: Write a script that executes a Terraform workspace deployment, captures the stdout logs, and logs success/failure status.
Click on the dropdown below to reveal the technical answer.
Answer:
import subprocess
import os
tf_dir = "/home/dev/DevOps/DevOps/12-Python-for-DevOps/terra-automate/terraform"
log_path = "/var/log/terraform_deploy.log"
def run_terraform():
print("Initiating Terraform deployment...")
try:
# Run apply command
process = subprocess.run(
f"terraform -chdir={tf_dir} apply -auto-approve",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True
)
# Log success
with open(log_path, "a") as log:
log.write(f"\n--- SUCCESS: {os.uname()[1]} ---\n")
log.write(process.stdout.decode())
print("Terraform applied successfully.")
except subprocess.CalledProcessError as e:
# Log failure details
with open(log_path, "a") as log:
log.write(f"\n--- FAILURE: {os.uname()[1]} ---\n")
log.write(e.stderr.decode())
print(f"Terraform deployment failed. Error logged to {log_path}.")
run_terraform()
❓ Scenario 4: Write a script that parses an application log file, extracts all "ERROR" entries, and generates a formatted markdown report.
Click on the dropdown below to reveal the technical answer.
Answer:
log_file = "app.log"
report_file = "error_report.md"
errors = []
# 1. Read and find errors
with open(log_file, "r") as f:
for line in f:
if "ERROR" in line:
errors.append(line.strip())
# 2. Write Markdown report
with open(report_file, "w") as f:
f.write("# 📋 DevOps Log Incident Report\n\n")
f.write(f"**Source Log File:** `{log_file}` \n")
f.write(f"**Total Incidents Found:** {len(errors)} \n\n")
f.write("## ⚠️ Identified Exceptions:\n")
if errors:
for idx, error in enumerate(errors, 1):
f.write(f"{idx}. `{error}`\n")
else:
f.write("*No errors detected in the current log range.*\n")
print(f"Report compiled successfully at {report_file}")
❓ Scenario 5: Write a script that takes the target environment as an input argument (
sys.argv) and runs environment-specific deployment commands.Click on the dropdown below to reveal the technical answer.
Answer:
import sys
def deploy(environment):
print(f"Initializing deployment configuration for: {environment.upper()}")
if environment == "dev":
print("Running: terraform apply -var-file=dev.tfvars")
elif environment == "stage":
print("Running: terraform apply -var-file=stage.tfvars")
elif environment == "prod":
print("CRITICAL: Executing Production deployment pipeline...")
print("Running: terraform apply -var-file=prod.tfvars")
else:
print(f"Error: Unknown environment '{environment}'!")
sys.exit(1)
if __name__ == "__main__":
# Ensure environment argument is provided
if len(sys.argv) < 2:
print("Usage: python deploy.py <dev|stage|prod>")
sys.exit(1)
target_env = sys.argv[1].lower()
deploy(target_env)
❓ Scenario 6: Write a script that creates timestamped daily backups, uploads them to AWS S3, and automatically purges old backups from the bucket that are older than 30 days.
Click on the dropdown below to reveal the technical answer.
Answer:
import os
import shutil
import boto3
from datetime import datetime, timezone, timedelta
# Config
BACKUP_DIR = "/opt/app/data"
S3_BUCKET = "my-company-backups"
date_suffix = datetime.now().strftime("%Y_%m_%d")
archive_name = f"/tmp/backup_{date_suffix}"
# 1. Create timestamped backup
archive_file = shutil.make_archive(archive_name, 'zip', BACKUP_DIR)
s3_key = os.path.basename(archive_file)
# Initialize AWS clients
s3 = boto3.client("s3")
s3_res = boto3.resource("s3")
try:
# 2. Upload to S3
s3.upload_file(archive_file, S3_BUCKET, s3_key)
print(f"Uploaded {s3_key} to S3 bucket.")
os.remove(archive_file) # Remove local copy
# 3. Purge objects older than 30 days
bucket = s3_res.Bucket(S3_BUCKET)
now_utc = datetime.now(timezone.utc)
expiration_limit = now_utc - timedelta(days=30)
for obj in bucket.objects.all():
if obj.last_modified < expiration_limit:
print(f"Purging expired backup object: {obj.key} (Modified: {obj.last_modified})")
obj.delete()
except Exception as e:
print(f"Backup operation failed: {e}")