Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.

The format follows [Conventional Commits](https://www.conventionalcommits.org/) and [Semantic Versioning](https://semver.org/): a new feature or capability bumps MINOR, a bug fix or performance change that leaves the public surface alone bumps PATCH, and docs/tests/chore-only commits don't bump. While the module is pre-1.0 a breaking change also bumps MINOR and is called out under a **Breaking Changes** heading — 1.0.0 is reserved for the point the public surface is declared stable, after which breaking changes bump MAJOR. There is intentionally no `[Unreleased]` section — every entry is dated at ship time.

## [0.7.3] - 2026-09-14

### Bug Fixes
Comment on lines +7 to +9

- **`Test-InforcerConnection` now returns `$true` / `$false`.** It previously wrote to the host and returned nothing at all, so `if (Test-InforcerConnection) { ... }` was always false — the `Test-*` verb promised a predicate the cmdlet never delivered. It now returns a boolean; the host output is unchanged.
- **Failures are warnings, not errors.** "Not connected" and a failed API call used to call `Write-Error`. That cannot coexist with a predicate: `Test-InforcerConnection -ErrorAction Stop` would throw instead of returning `$false`, as would any call once `$ErrorActionPreference` is `Stop` at global scope. (A script-scope `$ErrorActionPreference` does *not* reach a module cmdlet, so that case was never affected.) Both paths now use `Write-Warning`.
- **Migration.** Anything reading `-ErrorVariable` from this cmdlet should read `-WarningVariable` instead. The sharper edge: a script running under `Stop` that called `Test-InforcerConnection` bare, relying on the throw to abort, now continues past it — check the return value instead, e.g. `if (-not (Test-InforcerConnection)) { throw 'no connection' }`. Nothing inside the module calls this cmdlet and neither does the MCP server, so the blast radius is your own scripts.

## [0.7.2] - 2026-09-11

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Disconnect-Inforcer
| ------------------------------ | ----------------------------------------------------------- |
| **Connect-Inforcer** | Establishes a secure connection to the Inforcer REST API. Supports `-FetchGraphData` to also connect Microsoft Graph. |
| **Disconnect-Inforcer** | Disconnects and clears the session (including Graph if connected). |
| **Test-InforcerConnection** | Tests the API connection. |
| **Test-InforcerConnection** | Tests the API connection; returns `$true` or `$false`. |
| **Get-InforcerTenant** | Retrieves tenant information (optional filter by TenantId). |
| **Get-InforcerBaseline** | Retrieves baseline groups and members. |
| **Get-InforcerTenantPolicies** | Retrieves policies for a specified tenant. |
Expand Down
71 changes: 67 additions & 4 deletions Tests/Consistency.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,73 @@ Describe 'No-silent-failure contract' {
$output | Should -Not -BeNullOrEmpty
}

It 'Test-InforcerConnection produces an error when not connected' {
$err = $null
Test-InforcerConnection -ErrorVariable err -ErrorAction SilentlyContinue
$err | Should -Not -BeNullOrEmpty -Because 'should report not connected, not return silence'
It 'Test-InforcerConnection warns and returns $false when not connected' {
$warn = $null
$result = Test-InforcerConnection -WarningVariable warn -WarningAction SilentlyContinue
$result | Should -BeOfType [bool]
$result | Should -BeFalse
$warn | Should -Not -BeNullOrEmpty -Because 'should report not connected, not return silence'
}

It 'Test-InforcerConnection answers instead of throwing under -ErrorAction Stop' {
# -ErrorAction Stop is the form that reaches a module cmdlet: a caller-scope
# $ErrorActionPreference does NOT propagate into module scope, so testing that
# would pass no matter which stream the cmdlet writes to.
$result = Test-InforcerConnection -ErrorAction Stop -WarningAction SilentlyContinue
$result | Should -BeFalse
}

It 'Test-InforcerConnection returns $true when the API responds' {
Mock Invoke-RestMethod { [PSCustomObject]@{ data = @() } } -ModuleName InforcerCommunity
& (Get-Module InforcerCommunity) {
$script:InforcerSession = @{
ApiKey = (ConvertTo-SecureString 'test-key' -AsPlainText -Force)
BaseUrl = 'https://api.test.com'
}
}
try {
$result = Test-InforcerConnection 6>$null
$result | Should -BeOfType [bool]
$result | Should -BeTrue
} finally {
& (Get-Module InforcerCommunity) { $script:InforcerSession = $null }
}
}

It 'Test-InforcerConnection returns $false and warns when the API call fails' {
Mock Invoke-RestMethod { throw 'HTTP 401 Unauthorized' } -ModuleName InforcerCommunity
& (Get-Module InforcerCommunity) {
$script:InforcerSession = @{
ApiKey = (ConvertTo-SecureString 'test-key' -AsPlainText -Force)
BaseUrl = 'https://api.test.com'
}
}
try {
$warn = $null
$result = Test-InforcerConnection -WarningVariable warn -WarningAction SilentlyContinue 6>$null
$result | Should -BeFalse
"$warn" | Should -Match '401'
} finally {
& (Get-Module InforcerCommunity) { $script:InforcerSession = $null }
}
}

It 'Test-InforcerConnection answers instead of throwing when the API call fails under -ErrorAction Stop' {
# The failure path is the one that matters: a Write-Error in the catch would throw here
# and the predicate would be unusable exactly when the connection is broken.
Mock Invoke-RestMethod { throw 'HTTP 500 Server Error' } -ModuleName InforcerCommunity
& (Get-Module InforcerCommunity) {
$script:InforcerSession = @{
ApiKey = (ConvertTo-SecureString 'test-key' -AsPlainText -Force)
BaseUrl = 'https://api.test.com'
}
}
try {
$result = Test-InforcerConnection -ErrorAction Stop -WarningAction SilentlyContinue 6>$null
$result | Should -BeFalse
} finally {
& (Get-Module InforcerCommunity) { $script:InforcerSession = $null }
}
}

It 'Get-InforcerTenant produces an error when not connected' {
Expand Down
19 changes: 15 additions & 4 deletions docs/CMDLET-REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ No active session to disconnect.

## Test-InforcerConnection

Tests the current API connection by sending a request to the API. Requires an active session (run `Connect-Inforcer` first).
Tests the current API connection by sending a request to the API, and returns `$true` or `$false` so the result can be used as a condition. Requires an active session (run `Connect-Inforcer` first).

Failures — including "no session" — are reported as **warnings**, not errors, so the cmdlet keeps answering the question instead of throwing in scripts that run under `$ErrorActionPreference = 'Stop'`.

**Required API scope(s)**: None (session management only)

Expand All @@ -90,15 +92,24 @@ Connect-Inforcer -ApiKey $env:INFORCER_API_KEY -Region uk
Test-InforcerConnection
```

```powershell
# Reconnect only when the current session is dead
if (-not (Test-InforcerConnection)) { Connect-Inforcer -ApiKey $key -Region uk }
```

### Example output

Success (written to host):
Returns `True`; the following is written to the host:

```
Connection successful. API is reachable.
SUCCESS! Connection is working.
```

When not connected, an error is written (e.g. "Not connected. To connect, run: Connect-Inforcer ...").
With no active session it returns `False` and writes a warning:

```
WARNING: Not connected. To connect, run: Connect-Inforcer -ApiKey <ApiKey> -Region <uk|eu|us|anz>
Comment thread
Copilot marked this conversation as resolved.
```

---

Expand Down
2 changes: 1 addition & 1 deletion module/InforcerCommunity.psd1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@{
RootModule = 'InforcerCommunity.psm1'
ModuleVersion = '0.7.2'
ModuleVersion = '0.7.3'
GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
Author = 'Roy Klooster'
Description = 'Community PowerShell module for the Inforcer API. Created by Roy Klooster. Not owned or officially maintained by Inforcer.'
Expand Down
23 changes: 16 additions & 7 deletions module/Public/Test-InforcerConnection.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@
.SYNOPSIS
Tests the Inforcer API connection.
.DESCRIPTION
Makes a test request to the /beta/baselines endpoint to verify the current session and API key work.
Requires an active session (run Connect-Inforcer first).
Makes a test request to the /beta/baselines endpoint to verify the current session and API key
work, and returns $true or $false so the result can be used as a condition.

Failures are reported as warnings rather than errors, so `Test-InforcerConnection -ErrorAction Stop`
still answers the question instead of throwing.
.EXAMPLE
Connect-Inforcer -ApiKey $env:INFORCER_API_KEY -Region uk; Test-InforcerConnection
Connects then verifies the connection.
.EXAMPLE
if (-not (Test-InforcerConnection)) { Connect-Inforcer -ApiKey $key -Region uk }
Reconnects only when the current session is dead.
.OUTPUTS
None. Writes success or failure to the host.
System.Boolean. $true when the API responded, $false when it did not or there is no session.
Status messages go to the host; failures to the warning stream and details to verbose.
.LINK
https://github.com/royklo/InforcerCommunity/blob/main/docs/CMDLET-REFERENCE.md#test-inforcerconnection
.LINK
Connect-Inforcer
#>
function Test-InforcerConnection {
[CmdletBinding()]
[OutputType([bool])]
param()

if (-not (Test-InforcerSession)) {
Write-Error -Message "Not connected. To connect, run: Connect-Inforcer -ApiKey <ApiKey> -Region <uk|eu|us|anz>" `
-ErrorId 'NotConnected' -Category ConnectionError
return
Write-Warning 'Not connected. To connect, run: Connect-Inforcer -ApiKey <ApiKey> -Region <uk|eu|us|anz>'
return $false
}

$uri = $script:InforcerSession.BaseUrl + '/beta/baselines'
Expand All @@ -36,9 +43,11 @@ try {
$headers = @{ 'Inf-Api-Key' = $apiKey; 'Accept' = 'application/json' }
$null = Invoke-RestMethod -Uri $uri -Method GET -Headers $headers -UseBasicParsing
Write-Host 'SUCCESS! Connection is working.' -ForegroundColor Green
return $true
} catch {
Write-Host 'FAILED! Connection test failed.' -ForegroundColor Red
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
Write-Error -Message $_.Exception.Message -ErrorId 'ConnectionTestFailed' -Category ConnectionError
Write-Warning "Inforcer connection test failed: $($_.Exception.Message)"
return $false
}
}