Mert Özen Further With Every Line

Blog

Mert Özen's blog posts on software development, backend, frontend, and DevOps.

Series
8 posts found #scripting
Testing Scripts and Running Them in CI: Pester 6, PSScriptAnalyzer, Cross-Platform and Moving from 5.1 to 7.x
DevOps PART 08

Testing Scripts and Running Them in CI: Pester 6, PSScriptAnalyzer, Cross-Platform and Moving from 5.1 to 7.x

In GitHub Actions, a shell: pwsh step only adds $ErrorActionPreference = 'stop'; even if dotnet test fails in the middle, the step stays green when the last native command returns 0, so keep the run block a single script call. Pester 6 tests and mocks for Bookstore.Ops, PSScriptAnalyzer rules, a Windows and Ubuntu matrix, cross-platform traps, and the differences when moving from 5.1 to 7.x and from 7.6 to 7.7.

15 Nov 2026 16 min 6
Managing Remote Servers: SSH-Based Remoting, Invoke-Command, Sessions and Keeping Credentials Out of Scripts
DevOps PART 07

Managing Remote Servers: SSH-Based Remoting, Invoke-Command, Sessions and Keeping Credentials Out of Scripts

In PowerShell remoting, the objects returned by Invoke-Command are serialized copies: properties come back, methods do not; do the remote work inside the script block. SSH-based remoting, $using:, sessions, the second hop, reading secrets from a vault with SecretManagement, and a rollout script that deploys Bookstore.Api to two servers one by one and stops at the first failure.

15 Nov 2026 13 min 7
Real Work: File System, REST APIs, JSON/CSV, Scheduled Tasks, Windows Services, IIS and Network Diagnostics on Windows
DevOps PART 06

Real Work: File System, REST APIs, JSON/CSV, Scheduled Tasks, Windows Services, IIS and Network Diagnostics on Windows

ConvertTo-Json only goes 2 levels deep by default; a nested body sent to an API silently turns into the string "System.Collections.Hashtable", so pass -Depth to every body. Bearer tokens, -MaximumRetryCount retrying a POST even on 401, $PSScriptRoot and CSV encoding, Windows Services, scheduled tasks, IIS and network commands, and finally a deploy script for Bookstore.Api with a backup and a way back.

15 Nov 2026 16 min 8
From Functions to Modules: Advanced Functions, [CmdletBinding()], Parameter Validation, Pipeline Input and -WhatIf
DevOps PART 05

From Functions to Modules: Advanced Functions, [CmdletBinding()], Parameter Validation, Pipeline Input and -WhatIf

In PowerShell, a function's result is not its return value but everything that lands on the success stream; advanced functions ([CmdletBinding()]) let you manage that, parameter validation, pipeline input and -WhatIf/-Confirm like a cmdlet. We write the Bookstore.Ops module with its manifest, show preferences that do not reach a module and the ShouldProcess trap in CI, and install it from a local repository with PSResourceGet.

15 Nov 2026 19 min 6
Error Handling: Terminating and Non-Terminating Errors, $ErrorActionPreference, try/catch and Native Command Exit Codes
DevOps PART 04

Error Handling: Terminating and Non-Terminating Errors, $ErrorActionPreference, try/catch and Native Command Exit Codes

In PowerShell, $ErrorActionPreference = 'Stop' only stops on PowerShell errors; a failing exit code from a native command such as dotnet test also needs $PSNativeCommandUseErrorActionPreference = $true, otherwise publish runs even when tests break and the script exits with 0. We cover the two kinds of errors, try/catch/finally and trap, Write-Error vs throw, stderr and 2>&1, Set-StrictMode, the set -euo pipefail counterparts and a Bookstore build script that stops at the first failure.

15 Nov 2026 16 min 6
Conditions, Loops and Using the Pipeline in Scripts: switch, foreach, Where-Object, ForEach-Object and -Parallel
DevOps PART 03

Conditions, Loops and Using the Pipeline in Scripts: switch, foreach, Where-Object, ForEach-Object and -Parallel

In PowerShell the foreach statement is the fastest on a collection in memory, while for data streaming from a file the pipeline does far less work because it can stop early; a break inside ForEach-Object skips the rest of the script. We show comparison operators, reading logs with switch -Regex, begin/process/end blocks and ForEach-Object -Parallel with a script that checks Bookstore's 200 book covers, along with the $using:, race condition and Format-Table traps.

15 Nov 2026 15 min 8
Variables, Types and Collections: [type] Casts, Arrays, Hashtables, PSCustomObject, Splatting and Strings
DevOps PART 02

Variables, Types and Collections: [type] Casts, Arrays, Hashtables, PSCustomObject, Splatting and Strings

In PowerShell the result of a comparison depends on the type of the value on the left, and Import-Csv reads every column as a string, so data has to be converted to real types up front. Building a publisher report from Bookstore's book CSV, we cover putting $null on the left, arrays and +=, List, hashtables, PSCustomObject, splatting, strings, number and date traps under a Turkish culture, and how a [decimal] cast gives a different result on PowerShell 7.7 because of .NET 11.

15 Nov 2026 18 min 7
Your First PowerShell Script: .ps1, Ways to Run It, #Requires, Comment-Based Help and pwsh vs powershell.exe
DevOps PART 01

Your First PowerShell Script: .ps1, Ways to Run It, #Requires, Comment-Based Help and pwsh vs powershell.exe

Called from inside PowerShell, a script runs in the same process in its own scope; called with pwsh -File, it runs in a new process and reports its result through the exit code, and Write-Error alone does not change that code. With Test-BookstoreHealth.ps1, which checks the Bookstore API, we show &, dot-sourcing, -File vs -Command, the shebang on Linux, #Requires, comment-based help, the differences between Windows PowerShell 5.1 and 7.6 LTS, and the BOM and CRLF traps.

15 Nov 2026 16 min 3
Blog Map