CogniSkill · PowerShell Foundations for IT Professionals
Content review copy. This does not replace the site theme. For SC-300, this preview shows additions; the existing teaching is retained on import.
This course teaches the language through a read-only reporting task. You will inspect commands, understand the objects they return, validate a small service inventory, and build a report whose missing and invalid values remain visible. The project does not start, stop or reconfigure services. Its first version uses invented CSV data, so a learner can run it without administrator access or access to a work computer.
Use PowerShell 7 for the supplied examples. Windows PowerShell 5.1 also has many of the commands discussed, but encoding defaults and some parameter behavior differ. Do not assume that a script tested in one edition behaves identically in another. Run $PSVersionTable and record the version when submitting your project. Open a normal user terminal. If a company policy prevents script execution, follow its approved process rather than weakening a computer-wide policy. You can practise the short language examples interactively.
Four chapters, with practical work
- Discover commands and inspect their objects
- Build a pipeline without losing the evidence
- A complete validated CSV report
- Tests, an independent extension and a handoff
Applied capability and learning route
Build an object-based reporting pipeline with explicit boundary and input handling.
Follow the lessons in order: establish the model, trace the supplied evidence, attempt the case, compare the worked solution, and complete the changed-condition project. The target is intermediate reasoning within this stated scope, not mastery of the entire profession or product. If a prerequisite is unfamiliar, pause and use the linked foundational teaching before attempting the project.
All teaching, explained answers and core synthetic practice are free. Some operational labs require separately available software, a licensed test tenant or an authorized lab; offline completion does not claim hands-on product proficiency.
How to complete this course
Study the worked examples, attempt the exercises before opening solutions, and submit your own project notes. Completion records are self-reported; practical competence requires the stated verification.
Discover commands and inspect their objects
Commands have a discoverable contract
PowerShell commands commonly use a verb-noun name. Get-Process communicates an inspection operation; Stop-Process communicates a change. A reassuring verb is a useful clue, but it is not a security guarantee. A script or function can use an innocent name and still change files or send data. Inspect the implementation and documentation before running unfamiliar code.
Start with discovery:
$PSVersionTable.PSVersion
Get-Command Get-Process
Get-Help Get-Process -Examples
Get-Process | Select-Object -First 3 -Property Id, ProcessName
Get-Process | Get-Member
Get-Command tells you what command PowerShell resolves, including whether it is a cmdlet, function, alias or application. Name resolution matters when a profile or imported module defines the same name. Get-Help describes the parameters and behavior. Some installations have incomplete local help; the absence of examples does not mean that a parameter is safe to invent. Consult the matching official documentation when local help is unavailable.
The final command inspects the type and members of the emitted process objects. A process row on screen is a presentation of an object. Its properties can include values not shown by the default display. Do not parse the spaces in the table to obtain a process name or identifier. Select the property directly.
Run the examples and record three observations: your PowerShell edition, the command type for Get-Process, and the type shown by Get-Member. Process names and identifiers differ between machines, so this exercise has no fixed process list. A successful result shows that you can explain the object structure without treating the displayed table as its storage format.
Variables, strings and explicit conversion
A variable name begins with $. Assignment stores a value or a collection, rather than a text line from a terminal. Values have types. The following examples demonstrate a difference that matters when reading CSV:
$left = '2'
$right = '3'
$left + $right
[int]$left + [int]$right
$left.GetType().Name
$machine = 'APP01'
"Checking $machine"
'Checking $machine'
The expected first two results are 23 and 5. A CSV field containing digits is still text until validated and converted. Double-quoted strings expand variables; single-quoted strings preserve the dollar sign and name. Use this distinction deliberately when building an error message or documenting a literal command.
A cast can fail. [int]'offline' cannot provide a meaningful service count. A cast can also accept representations that your business rule should reject. The language's ability to convert a value and the report's decision to accept it are separate questions. Our report will first check that an uptime field contains a non-negative whole number. Only then will it convert the field.
Properties are a better interface than columns on screen
Create a small object:
$item = [pscustomobject]@{
Host = 'APP01'
Service = 'BackupAgent'
Status = 'Running'
UptimeHours = 12
}
$item.Host
$item | Select-Object Host, Service, Status
$item | Get-Member
The property names form the report's contract. A consumer can select Host even if a terminal is too narrow to display every property. A property called UptimeHours communicates a unit; a property called Value would force the reader to infer the unit from surrounding prose. Good naming reduces interpretation errors.
An ordered collection of property names is useful for export. Do not rely on a screenshot's column order or on the current console width. The final report will explicitly select its columns before writing CSV.
Practise before proceeding
Make a second object for APP02, service BackupAgent, status Stopped, and uptime 0. Predict whether printing the object will prove that the real service is stopped. It will not: you have created a fixture, not inspected a live system. Label it as synthetic evidence.
Change the status to lower-case running. Record whether comparing it with Running changes the result. PowerShell's usual string equality operators are case-insensitive; case-sensitive variants such as -ceq exist. Our report will normalize accepted status names so that the output is consistent. Normalization must not make an unsupported status such as StartingMaybe valid.
Reasoning model
A PowerShell pipeline carries objects until you deliberately format or serialize them. Keep selection, calculation and filtering before presentation. CSV values commonly enter as text, so validate and convert numeric fields before arithmetic. Define thresholds precisely: “less than 10%” differs from “10% or less.” Missing or zero capacity is not a healthy result; represent invalid input separately so the report cannot silently convert absence into reassurance.
Official reference for further study
Build a pipeline without losing the evidence
A pipeline passes objects
Create a fixture collection:
$services = @(
[pscustomobject]@{ Host='APP01'; Service='BackupAgent'; Status='Running'; UptimeHours=12 }
[pscustomobject]@{ Host='APP02'; Service='BackupAgent'; Status='Stopped'; UptimeHours=0 }
[pscustomobject]@{ Host='APP03'; Service='BackupAgent'; Status='Unknown'; UptimeHours=$null }
)
$services | Where-Object Status -eq 'Stopped' | Select-Object Host, Service
$services | Group-Object Status | Select-Object Name, Count
The first pipeline emits one row for APP02. The second emits three groups with one item each. The input objects remain unchanged. Filtering a report is different from modifying the source or remediating a service.
Where-Object makes a decision about each input object. Select-Object chooses the properties to emit. Group-Object changes the grain: instead of one item per service observation, you get one group per distinct status. Explain this change before adding a total to your report. A count of observations is not automatically a count of unique hosts.
If one host appears twice in a file, counting rows overstates the host population. Our first report will preserve duplicate observations, rather than silently remove them. The project extension requires a documented identity and duplicate policy.
The single-item collection trap
PowerShell can unwrap a command's single result. Use an array subexpression when the rest of your code requires a collection:
$stopped = @($services | Where-Object Status -eq 'Stopped')
$stopped.Count
$none = @($services | Where-Object Status -eq 'Missing')
$none.Count
Expected counts are 1 and 0. @(...) gives the downstream code a consistent collection shape when the command produces zero, one or several objects. That reduces fragile branching around .Count and indexing.
An empty result is not always good news. A completed, authorized search finding no stopped services differs from a collection that failed before returning data. The report should carry collection status when connected to a real source. For the offline fixture, an empty file is an invalid input because it cannot demonstrate the expected observations.
Formatting belongs at the end of a display pipeline
A formatting command emits instructions for the display system. It does not preserve the original object contract for further data processing. This is a common reporting defect:
$services | Select-Object Host, Service, Status | Format-Table
$services | Select-Object Host, Service, Status | ConvertTo-Csv -NoTypeInformation
The first output is suitable for a human looking at the terminal. The second output is CSV text with the requested column names. Do not insert Format-Table before Export-Csv; the export would receive formatting objects rather than service observations. A report that looks correct on screen can still have an unusable file.
Compare the CSV header with your predicted schema. It should name Host, Service, and Status. Open the resulting text rather than assuming the export is correct because the console looked tidy.
Functions make a decision independently testable
Separate the classification rule from file input and output. A function should return a small object representing its result. Avoid sending progress narration into the success output stream: a string saying “Working…” would become an extra pipeline item. Use a separate diagnostic stream for information a reader needs but a downstream CSV parser should not receive.
Our function will emit exactly one object per observation. It will use Assessment values Review, NoFinding, or Invalid. These names describe a reporting decision. NoFinding does not prove that a machine is healthy; it means that this rule found no reportable condition in the supplied fields.
Trace the boundary manually
The rule is: a stopped service needs review; a running service with uptime below 2 hours needs review; other valid running rows have no finding. Write expected results for uptime 0, 1, 2 and 3 before reading the implementation. At exactly 2 hours, a running service must have NoFinding because “below 2” is strict. A deliberate change from -lt to -le should break your boundary test.
An unknown service status remains invalid. It is not healthy and it is not proof of a stopped service. Keeping that distinction makes the report useful for troubleshooting the collector as well as the endpoint.
Evidence laboratory — attempt before reading the solution
Synthetic disk rows: A SizeGB=100 FreeGB=9; B SizeGB=200 FreeGB=20; C SizeGB=0 FreeGB=0; D SizeGB=100 FreeGB=blank. Rule: alert only when valid free percentage is strictly below 10. Compute expected states before coding. Design an object with Name, FreePercent and State. Then write a function that validates required fields, performs numeric arithmetic and emits one object per input. Do not query or alter real disks for this exercise.
Official reference for further study
A complete validated CSV report
Prepare the source file
Save the following fixture as services.csv in a practice directory. Save the script after it as Report-Services.ps1. Do not use a production export for your first run.
Host,Service,Status,UptimeHours
APP01,BackupAgent,Running,12
APP02,BackupAgent,Stopped,0
APP03,BackupAgent,Running,1
APP04,BackupAgent,Running,2
APP05,BackupAgent,Unknown,
APP06,BackupAgent,Running,offline
Before running the script, predict the output for each row. You should get six output rows: two Review, two NoFinding, and two Invalid. Explain each classification before comparing your answer with the exported file. Reconcile the rows rather than accepting a plausible total from a tutorial or a generated answer.
The following implementation makes no endpoint changes. It writes a report to a new file and refuses to overwrite an existing file.
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$InputPath,
[Parameter(Mandatory)][string]$OutputPath
)
function Get-ServiceAssessment {
param([Parameter(Mandatory)]$Row)
$hostName = ([string]$Row.Host).Trim()
$serviceName = ([string]$Row.Service).Trim()
$state = ([string]$Row.Status).Trim()
$rawUptime = ([string]$Row.UptimeHours).Trim()
$reason = ''
$assessment = 'Invalid'
$uptime = 0
if ($hostName -eq '' -or $serviceName -eq '') {
$reason = 'Missing identity'
} elseif ($state -notin @('Running','Stopped')) {
$reason = 'Unsupported status'
} elseif ($rawUptime -notmatch '^[0-9]+$' -or
-not [int]::TryParse($rawUptime, [ref]$uptime)) {
$reason = 'Uptime must be a non-negative 32-bit whole number'
} else {
$state = if ($state -eq 'Running') { 'Running' } else { 'Stopped' }
if ($state -eq 'Stopped') {
$assessment = 'Review'
$reason = 'Service is stopped in the observation'
} elseif ($uptime -lt 2) {
$assessment = 'Review'
$reason = 'Running service has less than 2 hours uptime'
} else {
$assessment = 'NoFinding'
$reason = 'No finding under this rule'
}
}
[pscustomobject]@{
Host = $hostName
Service = $serviceName
Status = $state
SourceUptimeHours = [string]$Row.UptimeHours
Assessment = $assessment
Reason = $reason
}
}
try {
$source = (Resolve-Path -LiteralPath $InputPath -ErrorAction Stop).Path
$destination = [System.IO.Path]::GetFullPath($OutputPath)
if ($source -eq $destination) { throw 'Input and output must differ' }
if (Test-Path -LiteralPath $destination) { throw 'Output already exists' }
$rows = @(Import-Csv -LiteralPath $source -Encoding utf8 -ErrorAction Stop)
if ($rows.Count -eq 0) { throw 'Input must contain observations' }
$actual = @($rows[0].PSObject.Properties.Name)
$required = @('Host','Service','Status','UptimeHours')
if (@(Compare-Object $required $actual).Count -ne 0) {
throw 'CSV columns do not match the expected schema'
}
$report = @($rows | ForEach-Object { Get-ServiceAssessment -Row $_ })
$report | Select-Object Host, Service, Status, SourceUptimeHours, Assessment, Reason |
Export-Csv -LiteralPath $destination -NoTypeInformation -Encoding utf8 -NoClobber -ErrorAction Stop
$report | Group-Object Assessment | Select-Object Name, Count
} catch {
Write-Error -Message $_.Exception.Message -ErrorAction Continue
exit 2
}
Run it in a child PowerShell process so a deliberate failing test cannot exit your interactive terminal:
pwsh -NoProfile -File ./Report-Services.ps1 -InputPath ./services.csv -OutputPath ./report.csv
$LASTEXITCODE
Import-Csv ./report.csv | Select-Object Host, Assessment, Reason
The normal exit code is 0. Read the exported file and reconcile its six observations with the fixture. APP02 and APP03 need review. APP01 and APP04 have no finding. APP05 and APP06 are invalid. The original raw uptime appears in SourceUptimeHours, which helps a reviewer diagnose why a row was rejected.
Why these choices matter
Resolve-Path -LiteralPath interprets the given path as a literal name and makes a missing source an explicit error. The script should not treat a file that never arrived as a healthy population of zero services. GetFullPath computes a destination path; it does not prove that its directory exists or that the caller can write there. Export remains an operation that can fail.
-ErrorAction Stop promotes relevant non-terminating errors to a terminating error that the catch block can handle. PowerShell has more than one error behavior. A failed command can emit an error and continue unless you intentionally establish a stopping boundary. Test that behavior rather than assuming that the existence of try guarantees every failure is caught.
-NoClobber protects an existing report at the export operation, not just at the preceding check. A file could appear between the check and the write. The final command must preserve the no-overwrite contract too.
TryParse avoids accepting a value outside the chosen 32-bit range. The regular expression narrows accepted text to digits: negative signs, decimal points and a word such as offline are invalid. This is a policy choice for this fixture, not a universal rule for all numeric data. A future schema could reasonably permit decimals; it would need a changed contract and independent tests.
The code checks the property names produced by Import-Csv. It does not implement a strict raw CSV parser that rejects every malformed row length or duplicate header before import. Do not describe it as complete file-format validation. Add a parser or an upstream validation step before operational use if those failure modes matter. A dependable course explains its example's limits instead of hiding them behind a “production ready” label.
Inject failures safely
Keep the original fixture unchanged and make separate test copies. Change APP04 uptime from 2 to 1; one result must move from NoFinding to Review. Change it to -1; it must become Invalid, not Review. Change APP06 to 2147483648; it exceeds the chosen integer range and must stay invalid. Delete the Service column; the script must fail with exit 2 rather than create a misleading report.
Run with an output file that already exists and compare its bytes afterward. It must remain unchanged. Run with a missing input file and confirm no report was created. Finally, pass the same file for both paths and verify the source remains unchanged. These are meaningful protections, not cosmetic tests of whether a button can be clicked.
The error stream is separate from the normal object output, but a caller can merge streams deliberately. A scheduler should inspect the exit code and capture diagnostics separately. It should not treat any text on standard output as proof that all rows succeeded.
Worked laboratory solution
Compare your reasoning with the worked solution
A is 9/100 × 100 = 9%, so Alert. B is exactly 10%, so OK under the strict rule. C cannot be divided by zero and must be Invalid. D is missing evidence and must also be Invalid, not 0% or healthy. The function should preserve that distinction. Use structured objects for later CSV export; applying Format-Table before Export-Csv exports formatting objects rather than the intended report data. Test your specific PowerShell version and culture-sensitive numeric input in a local lab.
Official reference for further study
Workbook already downloaded
Tests, an independent extension and a handoff
Write expected results before copying the rule
A useful test compares the program with an independently stated result. Repeating the implementation inside the test can reproduce the same defect twice. The following check executes the saved script using the six-row fixture and checks literal expected outcomes.
$expected = @{
APP01='NoFinding'; APP02='Review'; APP03='Review'
APP04='NoFinding'; APP05='Invalid'; APP06='Invalid'
}
$output = Join-Path ([System.IO.Path]::GetTempPath()) ('ck-' + [guid]::NewGuid() + '.csv')
try {
& pwsh -NoProfile -File ./Report-Services.ps1 -InputPath ./services.csv -OutputPath $output
if ($LASTEXITCODE -ne 0) { throw 'Baseline command failed' }
$actual = @(Import-Csv -LiteralPath $output)
if ($actual.Count -ne 6) { throw 'Expected exactly six report rows' }
if (@($actual | Group-Object Host | Where-Object Count -ne 1).Count -ne 0) {
throw 'Baseline must contain each expected host exactly once'
}
foreach ($hostName in $expected.Keys) {
if ($hostName -notin $actual.Host) { throw "Missing expected host $hostName" }
}
foreach ($row in $actual) {
if (-not $expected.ContainsKey($row.Host) -or $row.Assessment -ne $expected[$row.Host]) {
throw "Unexpected assessment for $($row.Host)"
}
}
'Six baseline assessments passed'
} finally {
if (Test-Path -LiteralPath $output) { Remove-Item -LiteralPath $output }
}
The baseline check also verifies that each expected host appears once. Merely checking six row assessments would miss a duplicate host replacing a missing host when both assessments look valid. The cleanup deletes only the unique temporary output created for this check. It does not use a wildcard over a shared directory. Even a test script should keep a narrow deletion boundary.
Change the classifier's -lt 2 to -le 2 in a separate copy. The test must fail for APP04. Restore the original and run it again. A deliberately introduced defect demonstrates that the test is sensitive to the rule it claims to verify.
This baseline check does not verify every schema error or file failure. Keep a second test table listing the negative cases from Chapter 3, their expected exit code, whether output should exist, and the source-file hash before and after. The combination is stronger than a single successful run.
Project A — Make the threshold configurable
Add a parameter named MinimumUptimeHours with a documented accepted range. Pass it into the classifier rather than reading a global variable. Keep the default at 2 so the baseline results remain unchanged. Test thresholds 0, 2 and 12, and test a negative value. Decide whether 0 means no running service can fail this uptime rule; explain that behavior in the README.
At threshold 12, APP01 with uptime 12 must still have NoFinding. APP03 and APP04 must need review. Stopped services remain reviewable independently of uptime. Invalid rows remain invalid. These expected results expose both boundary mistakes and accidental coupling between the status and uptime rules.
Project B — Detect duplicate observation identities
Define a key of host and service name after your documented normalization. A duplicate key means two observations describe the same service identity in this file. Do not choose the “latest” row when the file has no timestamp. Preserve both rows in a rejection report or fail the batch; state which policy you chose.
Add two fixtures: the same key with conflicting states, and a host with two different service names. The first must trigger your duplicate policy. The second must not be incorrectly collapsed into one service. A host count and a service count are different quantities.
Project C — Plan a live read-only adapter
Keep the classifier separate and design a collector that supplies the same fields. Research which properties your target platform can actually provide. Get-Service is not a universal source of an accurate uptime duration on every system. If uptime cannot be established, mark it unavailable rather than inventing a value of zero.
Document the collection identity, platform, collection time, failed targets and expected population. A collector that reached two of ten machines should not publish a “healthy fleet” conclusion based on the two successful responses. This extension is a design task until you test it on an authorized environment; the course's offline fixture does not validate remote collection.
Handoff acceptance criteria
Submit the source fixture, script, baseline report, a separate failure-results table and a short README. The README must state the row grain, accepted states, numeric rule, threshold boundary, output overwrite policy and validation limitations. A reviewer should be able to reproduce the baseline without changing machine configuration.
Explain why a stopped service observation requests review rather than authorizing an automatic restart. The service may be intentionally disabled or under maintenance; restarting it changes the environment and requires additional context and authority. Explain why an invalid observation cannot be counted as healthy. Explain why formatting before CSV export breaks a data pipeline even when the console looks attractive.
You have completed the course when you can change a requirement, predict the changed results, expose a deliberate defect and defend your reporting boundary. Completion of the website's lesson check records study progress; it does not certify that you can administer an unfamiliar production environment.
Reference: https://learn.microsoft.com/en-us/powershell/scripting/learn/ps101/00-introduction
Transfer project — changed conditions
Add E with FreeGB greater than SizeGB and F with a negative value. Extend validation with an explicit policy for inconsistent data. Write tests for 9.99%, exactly 10%, 10.01%, zero and missing input. Export the structured result, import it again and check field names and values. Explain which steps are presentation-only and why a clean-looking table does not prove the underlying calculation.
Practical assessment rubric
Score each dimension 0–2: 0 = absent or materially incorrect; 1 = partly correct with an unresolved gap; 2 = correct, explicit and supported by the supplied evidence or an actual labeled lab result.
- Evidence/model: identify the relevant entities, inputs, assumptions and boundaries without inventing facts.
- Main result: derive the case-specific expected result, calculation or decision and show the intermediate reasoning.
- Failure/boundary: test the changed condition and explain why the original conclusion does or does not transfer.
- Verification: supply reproducible steps and expected/observed results; label manual traces as manual, not executed.
- Communication/safety: explain limitations, owners and a proportionate next action without unsafe or unnecessary access.
Target: at least 8/10, with no zero in main result or communication/safety. This is a local learning rubric, not an accredited certification standard. A written/manual solution demonstrates reasoning only; operational competence additionally requires the relevant executable lab in an authorized environment. The native multiple-choice score is separate and does not automatically grade this rubric.
Submission template
- My input/evidence and assumptions:
- Expected result with calculations or trace:
- My implementation/decision and justification:
- Changed-condition result:
- Observed execution, or explicitly labeled manual trace:
- Failure and recovery evidence:
- Unverified limitation and next action:
- Self-score with evidence for each dimension:
Remediation
If the main result is wrong, reconstruct the supplied case from the raw inputs before changing code or controls. If the boundary case is wrong, identify the assumption that changed and rerun both the original and changed case. If verification is missing, do not convert a prediction into an observed result. Reattempt the transfer task without copying the worked solution, then explain it to another person.
Official reference for further study
Assessment: explain your decisions
Choose an answer and write your reason before opening each explanation. Then explain why the other choices do not fit the evidence. Use missed questions to revisit the lesson. This knowledge check is only one part of assessment: complete the practical transfer project as well. It does not predict certification-exam success.
1. What generally flows through this pipeline?
- Objects
- Only screenshots
- Only shell text
Answer and explanation
Objects — PowerShell passes structured objects between these commands.
2. Where should Format-Table normally go?
- Before every filter
- At the display end
- Inside CSV records
Answer and explanation
At the display end — Formatting objects are intended for presentation.
3. What does -ExpandProperty Name return?
- All object methods
- A new administrator role
- The Name values
Answer and explanation
The Name values — Expansion returns property values rather than selected wrapper objects.
4. Your console table looks correct, but the CSV has formatting-property columns. Which change repairs the data pipeline?
- Remove Format-Table before export and select the intended object properties.
- Increase terminal width before running Export-Csv.
- Parse the rendered spaces to reconstruct rows.
Answer and explanation
Remove Format-Table before export and select the intended object properties. — Formatting emits display instructions rather than the original service objects. Select-Object preserves a data interface; terminal width changes presentation and does not restore that interface.
5. A running service has uptime exactly 2 hours under a below-2-hour review rule. What is expected?
- Review because the duration is low.
- NoFinding for this rule, while broader health remains unproven.
- Invalid because uptime must be greater than 2.
Answer and explanation
NoFinding for this rule, while broader health remains unproven. — The strict condition is -lt 2. NoFinding is the classifier result for the supplied fields; it does not prove service health, availability or correct configuration.
6. A real service collector cannot establish uptime for one target. Which export is defensible?
- Write zero so the report always has an integer.
- Omit the machine and describe all collected machines as healthy.
- Keep the value unavailable and carry the collection limitation.
Answer and explanation
Keep the value unavailable and carry the collection limitation. — Unavailable evidence is different from measured zero. A live adapter needs collection-status and coverage evidence; the synthetic fixture does not validate every platform property.
7. What is B’s state under the stated rule?
- OK at exactly 10%.
- Alert because the threshold is inclusive.
- Invalid because every round number is invalid.
Answer and explanation
OK at exactly 10%. — The rule is strictly below 10, not less than or equal to 10. Boundary tests protect this distinction.
8. How should C be represented?
- Alert at a safely computed 0%.
- Invalid because capacity is zero.
- Healthy at 100% free.
Answer and explanation
Invalid because capacity is zero. — Division by zero has no valid percentage here. Reporting a health state would hide invalid source data.
9. Where should Format-Table occur?
- Before Export-Csv to preserve raw objects.
- Before numeric validation.
- At presentation time, after the data pipeline used for export.
Answer and explanation
At presentation time, after the data pipeline used for export. — Formatting changes the pipeline into formatting instructions. Keep report objects intact for calculations and serialization.
Use the explained questions above for offline practice. A scored attempt requires the course assessment on CogniSkill.ca.