diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..798f65a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,271 @@ +# Build, test and release the IdentityCommand.SIA PowerShell module. +# +# Repository secrets (Settings > Secrets and variables > Actions), mapped into steps via env: +# CODECOV_TOKEN - Codecov upload token +# access_token - GitHub fine-grained token, Contents read/write (version commit + GitHub Release) +# git_email - commit author email for the version commit (mapped to env github_email; secret names cannot start with GITHUB_) +# psgallery_key - PowerShell Gallery API key + +name: CI + +on: + push: + branches: + - '**' + paths: + - 'IdentityCommand.SIA/**' + - 'Tests/**' + # PRs also run on CHANGELOG.md and workflow edits: the CHANGELOG decides the release version. + pull_request: + branches: + - main + - master + - vNext + paths: + - 'IdentityCommand.SIA/**' + - 'Tests/**' + - CHANGELOG.md + - '.github/workflows/ci.yml' + # Manual runs ignore path filters. + workflow_dispatch: + +# One run at a time per ref, so release version commits cannot race. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + +env: + MODULE_NAME: IdentityCommand.SIA + # pspete.Build tag or branch cloned into ./build by each job. + BUILD_REF: v1 + MODULE_GUID: '634faae6-34c8-4887-a8fb-be9951c536f4' + GIT_USER_NAME: 'Pete Maan' + RELEASE_VERSION_GATE: '0.1.0' + # Stable release notes are written to a post per major.minor version in this folder; empty for none. + RELEASE_NOTES_PATH: 'docs/collections/_posts' + # vNext releases are prereleases: -preview. + PRERELEASE_LABEL: ${{ github.ref == 'refs/heads/vNext' && 'preview' || '' }} + # IdentityCommand version installed for tests and packaging; the module copies its private helpers. + IDENTITYCOMMAND_VERSION: '0.7.159' + +defaults: + run: + shell: pwsh + +jobs: + test: + name: Test Source (Code Coverage) + runs-on: windows-latest + steps: + - uses: actions/checkout@v7 + - name: Get Build Scripts + run: git clone --depth 1 --branch $env:BUILD_REF https://github.com/pspete/pspete.Build build + - name: Install Dependencies + run: ./build/install.ps1 + - name: Install IdentityCommand + run: | + . ./build/retry.ps1 + Invoke-Retry { Install-PSResource -Name IdentityCommand -Version $env:IDENTITYCOMMAND_VERSION -Repository PSGallery -TrustRepository -ErrorAction Stop } + # Help is generated locally with platyPS 0.14 and committed; PlatyPS 1.x output regresses it. + #- name: Update External Help + # run: ./build/help.ps1 -ModuleName $env:MODULE_NAME + - name: Test Source + run: ./build/test.ps1 -CodeCoverage -ModuleGuid $env:MODULE_GUID + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Upload Test Results + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v7 + with: + name: test-results-source + path: TestResults.xml + overwrite: true + + changelog: + name: CHANGELOG Entry + if: github.event_name == 'pull_request' + runs-on: windows-latest + steps: + - uses: actions/checkout@v7 + - name: Get Build Scripts + run: git clone --depth 1 --branch $env:BUILD_REF https://github.com/pspete/pspete.Build build + - name: Check Unreleased Notes + run: ./build/changelog.ps1 + + # Build and built module tests run for main/master/vNext and PRs (the pull_request trigger only includes main/master/vNext). + build: + name: Build + if: >- + github.event_name == 'pull_request' || + contains(fromJSON('["refs/heads/main", "refs/heads/master", "refs/heads/vNext"]'), github.ref) + runs-on: windows-latest + outputs: + moduleVersion: ${{ steps.version.outputs.moduleVersion }} + releaseVersion: ${{ steps.version.outputs.releaseVersion }} + steps: + - uses: actions/checkout@v7 + - name: Get Build Scripts + run: git clone --depth 1 --branch $env:BUILD_REF https://github.com/pspete/pspete.Build build + # moduleVersion is the manifest ModuleVersion (package folder name); releaseVersion adds any prerelease label. + - name: Set Module Version + id: version + run: | + $Version = ./build/version.ps1 -ModuleName $env:MODULE_NAME -PrereleaseLabel "$env:PRERELEASE_LABEL" + $ModuleVersion = ($Version -split '-')[0] + "moduleVersion=$ModuleVersion" >> $env:GITHUB_OUTPUT + "releaseVersion=$Version" >> $env:GITHUB_OUTPUT + "### $env:MODULE_NAME $Version" >> $env:GITHUB_STEP_SUMMARY + # Help is generated locally with platyPS 0.14 and committed; PlatyPS 1.x output regresses it. + #- name: Update External Help + # run: ./build/help.ps1 -ModuleName $env:MODULE_NAME + - name: Build Module + run: >- + ./build/build.ps1 + -ModuleName $env:MODULE_NAME + -BuildVersion '${{ steps.version.outputs.releaseVersion }}' + -OutputFolder "$env:RUNNER_TEMP/staging" + - name: Upload Built Module + uses: actions/upload-artifact@v7 + with: + name: module + path: ${{ runner.temp }}/staging/${{ env.MODULE_NAME }} + overwrite: true + + test-module: + name: Test Built Module (${{ matrix.name }}) + needs: build + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: PowerShell + os: windows-latest + shell: pwsh + - name: WindowsPowerShell + os: windows-latest + shell: powershell + - name: Linux + os: ubuntu-latest + shell: pwsh + #- name: Mac + # os: macos-latest + # shell: pwsh + defaults: + run: + shell: ${{ matrix.shell }} + steps: + - uses: actions/checkout@v7 + - name: Remove Module Source + run: Remove-Item -Path "./$env:MODULE_NAME" -Recurse -Force + - name: Get Build Scripts + run: git clone --depth 1 --branch $env:BUILD_REF https://github.com/pspete/pspete.Build build + - name: Download Built Module + uses: actions/download-artifact@v8 + with: + name: module + path: ${{ env.MODULE_NAME }} + # Windows PowerShell 5.1 has no PSResourceGet; install.ps1 skips its ~60s bootstrap when the module is found. + # The key rolls monthly so a newer PSResourceGet is picked up. + - name: Cache Month + id: month + if: matrix.shell == 'powershell' + run: '"month=$(Get-Date -Format yyyy-MM)" >> $env:GITHUB_OUTPUT' + - name: Cache PSResourceGet + if: matrix.shell == 'powershell' + uses: actions/cache@v6 + with: + path: ~/Documents/WindowsPowerShell/Modules/Microsoft.PowerShell.PSResourceGet + key: psresourceget-winps-${{ steps.month.outputs.month }} + - name: Install Dependencies + run: ./build/install.ps1 + - name: Install IdentityCommand + run: | + . ./build/retry.ps1 + Invoke-Retry { Install-PSResource -Name IdentityCommand -Version $env:IDENTITYCOMMAND_VERSION -Repository PSGallery -TrustRepository -ErrorAction Stop } + - name: Test Built Module + run: ./build/test.ps1 -ModuleGuid $env:MODULE_GUID + - name: Upload Test Results + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v7 + with: + name: test-results-${{ matrix.name }} + path: TestResults.xml + overwrite: true + + # Release runs only for pushes and manual runs on main/master (stable) and vNext (prerelease), never for PRs. + release: + name: Release + needs: + - test + - build + - test-module + if: >- + github.event_name != 'pull_request' && + contains(fromJSON('["refs/heads/main", "refs/heads/master", "refs/heads/vNext"]'), github.ref) + runs-on: windows-latest + env: + MODULE_VERSION: ${{ needs.build.outputs.moduleVersion }} + RELEASE_VERSION: ${{ needs.build.outputs.releaseVersion }} + steps: + # deploy-github.ps1 pushes with access_token; the checkout token must not override it. + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Get Build Scripts + run: git clone --depth 1 --branch $env:BUILD_REF https://github.com/pspete/pspete.Build build + - name: Download Built Module + uses: actions/download-artifact@v8 + with: + name: module + path: ${{ runner.temp }}/Package/${{ env.MODULE_NAME }}/${{ env.MODULE_VERSION }} + - name: Install IdentityCommand + run: | + . ./build/retry.ps1 + Invoke-Retry { Install-PSResource -Name IdentityCommand -Version $env:IDENTITYCOMMAND_VERSION -Repository PSGallery -TrustRepository -ErrorAction Stop } + + # Code signing of the packaged module goes here, before it is archived and published. + + - name: Archive Module + run: >- + Compress-Archive + -Path "$env:RUNNER_TEMP/Package/$env:MODULE_NAME" + -DestinationPath "$env:RUNNER_TEMP/$env:MODULE_NAME-v$env:RELEASE_VERSION.zip" + -Force + - name: Upload Module Package + uses: actions/upload-artifact@v7 + with: + name: package + path: ${{ runner.temp }}/${{ env.MODULE_NAME }}-v${{ env.RELEASE_VERSION }}.zip + overwrite: true + - name: Deploy to GitHub + run: >- + ./build/deploy-github.ps1 + -ModuleName $env:MODULE_NAME + -Version $env:RELEASE_VERSION + -Branch '${{ github.ref_name }}' + -Repository '${{ github.repository }}' + -ModulePath "$env:RUNNER_TEMP/Package/$env:MODULE_NAME/$env:MODULE_VERSION" + -ArchivePath "$env:RUNNER_TEMP/$env:MODULE_NAME-v$env:RELEASE_VERSION.zip" + -ReleaseVersionGate $env:RELEASE_VERSION_GATE + -GitUserName $env:GIT_USER_NAME + -ReleaseNotesPath $env:RELEASE_NOTES_PATH + env: + access_token: ${{ secrets.access_token }} + github_email: ${{ secrets.git_email }} + # The commit message is passed via env so quotes in it cannot break the command line. + - name: Deploy to PowerShell Gallery + run: >- + ./build/deploy-psgallery.ps1 + -ModuleName $env:MODULE_NAME + -Version $env:RELEASE_VERSION + -Branch '${{ github.ref_name }}' + -ModulePath "$env:RUNNER_TEMP/Package/$env:MODULE_NAME/$env:MODULE_VERSION" + -ReleaseVersionGate $env:RELEASE_VERSION_GATE + -CommitMessage $env:COMMIT_MESSAGE + env: + psgallery_key: ${{ secrets.psgallery_key }} + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} diff --git a/.gitignore b/.gitignore index 7f4fba2..f2fcb97 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ CLAUDE.md coverage.xml TestResults.xml -codecov.exe \ No newline at end of file +codecov.exe +build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 7132cbc..a8a2b48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,17 @@ All notable changes to this project will be documented in this file. ## Unreleased -- N/A +### Changed + +- Moved to the pspete.Build GitHub Actions pipeline; AppVeyor is retired. +- Requires IdentityCommand 0.7.159 or later. +- Argument completer registrations run from the module psm1, after IdentityCommand's helpers are copied in. + +### Fixed + +- `New-SIAPolicyProviderDefinition` help: `-vnetIds` now has a description, and `-vpcIds` describes AWS/GCP VPC IDs instead of Azure VNet IDs. -## 0.6.67 - 07-10-2026 +## [0.6.67] - 2026-10-07 ### Added @@ -18,7 +26,7 @@ All notable changes to this project will be documented in this file. - The module loader copies `IdentityCommand`'s private helper functions from the loaded module's session state instead of dot-sourcing its `Private` folder, so it works with both the current `IdentityCommand` layout and the combined single-file layout of future releases. Each copied helper runs in this module's scope and uses its session. -## 0.5.60 - 07-09-2026 +## [0.5.60] - 2026-09-07 ### Changed @@ -32,7 +40,7 @@ All notable changes to this project will be documented in this file. - The module loader resolves a single `IdentityCommand` module - the highest version - when more than one version is loaded. Previously every loaded version's `Private` folder was dot-sourced, last one winning. - Importing `IdentityCommand.SIA` without `IdentityCommand` loaded now throws a clear error instead of failing obscurely. -## 0.4.50 - 5-9-2026 +## [0.4.50] - 2026-09-05 ### Added @@ -49,7 +57,7 @@ All notable changes to this project will be documented in this file. - `Get-SIADatabaseStrongAccount`, `Get-SIADatabaseTarget` **(breaking)**: `-cursor` removed - the commands now page results internally. - `Set-SIAPolicy`: `-policyId` parameter renamed `-policyid` for consistency with `Get-SIAPolicy` / `Remove-SIAPolicy`. -## 0.3.32 - 01-09-2026 +## [0.3.32] - 2026-09-01 ### Security @@ -101,7 +109,7 @@ All notable changes to this project will be documented in this file. - `Get-SIAResource` - Updated to return correct property of output value. -## 0.2.11 - 05-03-2024 +## [0.2.11] - 2024-03-05 ### Added @@ -117,7 +125,7 @@ All notable changes to this project will be documented in this file. - N/A -## 0.1.10 - 03-03-2024 +## [0.1.10] - 2024-03-03 ### Added @@ -131,7 +139,7 @@ All notable changes to this project will be documented in this file. - N/A -## 0.1.9 - 03-03-2024 +## [0.1.9] - 2024-03-03 Initial release of `IdentityCommand.DPA` module diff --git a/IdentityCommand.SIA/IdentityCommand.SIA.psd1 b/IdentityCommand.SIA/IdentityCommand.SIA.psd1 index dcd48eb..eee4499 100644 --- a/IdentityCommand.SIA/IdentityCommand.SIA.psd1 +++ b/IdentityCommand.SIA/IdentityCommand.SIA.psd1 @@ -52,7 +52,7 @@ # Modules that must be imported into the global environment prior to importing this module RequiredModules = @( - @{ModuleName = 'IdentityCommand'; GUID = 'f6bb2bdf-0c53-443f-be07-3c25264a09b1'; ModuleVersion = '0.6.158' } + @{ModuleName = 'IdentityCommand'; GUID = 'f6bb2bdf-0c53-443f-be07-3c25264a09b1'; ModuleVersion = '0.7.159' } ) # Assemblies that must be loaded prior to importing this module diff --git a/IdentityCommand.SIA/IdentityCommand.SIA.psm1 b/IdentityCommand.SIA/IdentityCommand.SIA.psm1 index e1e2f7f..1b065b5 100644 --- a/IdentityCommand.SIA/IdentityCommand.SIA.psm1 +++ b/IdentityCommand.SIA/IdentityCommand.SIA.psm1 @@ -1,4 +1,5 @@ -<# +#region Loader +<# .SYNOPSIS .DESCRIPTION @@ -16,28 +17,6 @@ param( ) -#Copy IdentityCommand's private helpers into this module first: this module's own files call them, -#and the argument completer registrations do so at import time. -#Each copy is created from the function definition, so it runs in this module's scope and uses this -#module's $ISPSSSession, whether IdentityCommand loaded from source or from its combined psm1. -#Resolve a single IdentityCommand module: with more than one version loaded, Get-Module returns -#an array. -$Module = Get-Module -Name IdentityCommand | Sort-Object Version -Descending | Select-Object -First 1 - -if ($null -eq $Module) { - throw 'The IdentityCommand module is not loaded. Import IdentityCommand and try again.' -} - -& $Module { Get-ChildItem -Path Function: } | - - Where-Object { $_.ModuleName -eq $Module.Name -and -not $Module.ExportedFunctions.ContainsKey($_.Name) } | - - ForEach-Object { - - . ([scriptblock]::Create("function $($_.Name) {$($_.Definition)}")) - - } - #Get function files Get-ChildItem $PSScriptRoot\ -Recurse -Include '*.ps1' -Exclude '*.ps1xml' | @@ -64,6 +43,98 @@ Get-ChildItem $PSScriptRoot\ -Recurse -Include '*.ps1' -Exclude '*.ps1xml' | } +#endregion Loader + +#Copy IdentityCommand's private helpers into this module: this module's functions call them, and +#the argument completer registrations below do so at import time. +#Each copy is created from the function definition, so it runs in this module's scope and uses this +#module's $ISPSSSession, whether IdentityCommand loaded from source or from its combined psm1. +#Resolve a single IdentityCommand module: with more than one version loaded, Get-Module returns +#an array. +$Module = Get-Module -Name IdentityCommand | Sort-Object Version -Descending | Select-Object -First 1 + +if ($null -eq $Module) { + throw 'The IdentityCommand module is not loaded. Import IdentityCommand and try again.' +} + +& $Module { Get-ChildItem -Path Function: } | + + Where-Object { $_.ModuleName -eq $Module.Name -and -not $Module.ExportedFunctions.ContainsKey($_.Name) } | + + ForEach-Object { + + . ([scriptblock]::Create("function $($_.Name) {$($_.Definition)}")) + + } + +#region Registration + +$SIAConnectorIdCompleter = Get-ArgumentCompleter -RetrievalCommand 'Get-SIAConnector' -ValueProperty 'id' -LabelProperty 'name' + +Register-ArgumentCompleter -ParameterName 'connector_id' -ScriptBlock $SIAConnectorIdCompleter -CommandName @( + 'Get-SIAConnector' + 'Remove-SIAConnector' + 'Update-SIAConnector' + 'Test-SIAConnector' + 'Set-SIAConnectorMaintenanceMode' + 'Invoke-SIAConnectorCertificateRotation' +) + +Register-ArgumentCompleter -ParameterName 'connectorId' -ScriptBlock $SIAConnectorIdCompleter -CommandName 'Add-SIAConnectorPoolMember' + +$SIAPolicyIdCompleter = Get-ArgumentCompleter -RetrievalCommand 'Get-SIAPolicy' -ValueProperty 'policyId' -LabelProperty 'policyName' + +#Get-SIAPolicy / Remove-SIAPolicy / Set-SIAPolicy. +foreach ($ParameterName in 'policyid') { + Register-ArgumentCompleter -ParameterName $ParameterName -ScriptBlock $SIAPolicyIdCompleter -CommandName @( + 'Get-SIAPolicy' + 'Set-SIAPolicy' + 'Remove-SIAPolicy' + ) +} + +Register-ArgumentCompleter -ParameterName 'policyName' -ScriptBlock ( + Get-ArgumentCompleter -RetrievalCommand 'Get-SIAPolicy' -ValueProperty 'policyName' +) -CommandName 'Set-SIAPolicy' + +Register-ArgumentCompleter -ParameterName 'strong_account_id' -ScriptBlock ( + Get-ArgumentCompleter -RetrievalCommand 'Get-SIADatabaseStrongAccount' -ValueProperty 'id' -LabelProperty 'name' +) -CommandName @( + 'Get-SIADatabaseStrongAccount' + 'Set-SIADatabaseStrongAccount' + 'Remove-SIADatabaseStrongAccount' +) + +$SIASecretIdCompleter = Get-ArgumentCompleter -RetrievalCommand 'Get-SIAStrongAccount' -ValueProperty 'secret_id' -LabelProperty 'secret_name' + +Register-ArgumentCompleter -ParameterName 'secret_id' -ScriptBlock $SIASecretIdCompleter -CommandName @( + 'Remove-SIAStrongAccount' + 'Set-SIAStrongAccount' + 'Add-SIATargetSet' +) + +#Get-SIATargetSet takes the strong account's secret_id under a differently-cased parameter name. +Register-ArgumentCompleter -ParameterName 'strongAccountId' -ScriptBlock $SIASecretIdCompleter -CommandName 'Get-SIATargetSet' + +Register-ArgumentCompleter -ParameterName 'name' -ScriptBlock ( + Get-ArgumentCompleter -RetrievalCommand 'Get-SIATargetSet' -ValueProperty 'name' -LabelProperty 'type' +) -CommandName @( + 'Get-SIATargetSet' + 'Remove-SIATargetSet' +) + +#Get-SIAHttpsRelay's item id field is unconfirmed against a live response - assumed 'id' by analogy +#with every other SIA list endpoint. No label property, for the same reason. +Register-ArgumentCompleter -ParameterName 'https_relay_id' -ScriptBlock ( + Get-ArgumentCompleter -RetrievalCommand 'Get-SIAHttpsRelay' -ValueProperty 'id' +) -CommandName @( + 'Remove-SIAHttpsRelay' + 'Update-SIAHttpsRelay' + 'Invoke-SIAHttpsRelayCertificateRotation' +) + +#endregion + # Script scope session object for session data $ISPSSSession = [ordered]@{ tenant_url = $null diff --git a/IdentityCommand.SIA/Private/SIAArgumentCompleter.ps1 b/IdentityCommand.SIA/Private/SIAArgumentCompleter.ps1 deleted file mode 100644 index 4298a6d..0000000 --- a/IdentityCommand.SIA/Private/SIAArgumentCompleter.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -#The completer helper functions this file used to define now live in IdentityCommand's -#Private folder, which the psm1 loads into this module's scope. - -#region Registration - -$SIAConnectorIdCompleter = Get-ArgumentCompleter -RetrievalCommand 'Get-SIAConnector' -ValueProperty 'id' -LabelProperty 'name' - -Register-ArgumentCompleter -ParameterName 'connector_id' -ScriptBlock $SIAConnectorIdCompleter -CommandName @( - 'Get-SIAConnector' - 'Remove-SIAConnector' - 'Update-SIAConnector' - 'Test-SIAConnector' - 'Set-SIAConnectorMaintenanceMode' - 'Invoke-SIAConnectorCertificateRotation' -) - -Register-ArgumentCompleter -ParameterName 'connectorId' -ScriptBlock $SIAConnectorIdCompleter -CommandName 'Add-SIAConnectorPoolMember' - -$SIAPolicyIdCompleter = Get-ArgumentCompleter -RetrievalCommand 'Get-SIAPolicy' -ValueProperty 'policyId' -LabelProperty 'policyName' - -#Get-SIAPolicy / Remove-SIAPolicy / Set-SIAPolicy. -foreach ($ParameterName in 'policyid') { - Register-ArgumentCompleter -ParameterName $ParameterName -ScriptBlock $SIAPolicyIdCompleter -CommandName @( - 'Get-SIAPolicy' - 'Set-SIAPolicy' - 'Remove-SIAPolicy' - ) -} - -Register-ArgumentCompleter -ParameterName 'policyName' -ScriptBlock ( - Get-ArgumentCompleter -RetrievalCommand 'Get-SIAPolicy' -ValueProperty 'policyName' -) -CommandName 'Set-SIAPolicy' - -Register-ArgumentCompleter -ParameterName 'strong_account_id' -ScriptBlock ( - Get-ArgumentCompleter -RetrievalCommand 'Get-SIADatabaseStrongAccount' -ValueProperty 'id' -LabelProperty 'name' -) -CommandName @( - 'Get-SIADatabaseStrongAccount' - 'Set-SIADatabaseStrongAccount' - 'Remove-SIADatabaseStrongAccount' -) - -$SIASecretIdCompleter = Get-ArgumentCompleter -RetrievalCommand 'Get-SIAStrongAccount' -ValueProperty 'secret_id' -LabelProperty 'secret_name' - -Register-ArgumentCompleter -ParameterName 'secret_id' -ScriptBlock $SIASecretIdCompleter -CommandName @( - 'Remove-SIAStrongAccount' - 'Set-SIAStrongAccount' - 'Add-SIATargetSet' -) - -#Get-SIATargetSet takes the strong account's secret_id under a differently-cased parameter name. -Register-ArgumentCompleter -ParameterName 'strongAccountId' -ScriptBlock $SIASecretIdCompleter -CommandName 'Get-SIATargetSet' - -Register-ArgumentCompleter -ParameterName 'name' -ScriptBlock ( - Get-ArgumentCompleter -RetrievalCommand 'Get-SIATargetSet' -ValueProperty 'name' -LabelProperty 'type' -) -CommandName @( - 'Get-SIATargetSet' - 'Remove-SIATargetSet' -) - -#Get-SIAHttpsRelay's item id field is unconfirmed against a live response - assumed 'id' by analogy -#with every other SIA list endpoint. No label property, for the same reason. -Register-ArgumentCompleter -ParameterName 'https_relay_id' -ScriptBlock ( - Get-ArgumentCompleter -RetrievalCommand 'Get-SIAHttpsRelay' -ValueProperty 'id' -) -CommandName @( - 'Remove-SIAHttpsRelay' - 'Update-SIAHttpsRelay' - 'Invoke-SIAHttpsRelayCertificateRotation' -) - -#endregion diff --git a/IdentityCommand.SIA/en-US/IdentityCommand.SIA-help.xml b/IdentityCommand.SIA/en-US/IdentityCommand.SIA-help.xml index aecfe2a..a3f0a9a 100644 --- a/IdentityCommand.SIA/en-US/IdentityCommand.SIA-help.xml +++ b/IdentityCommand.SIA/en-US/IdentityCommand.SIA-help.xml @@ -5793,8 +5793,8 @@ $FQDNrules += New-SIAPolicyFQDNRuleDefinition -operator PREFIX -computernamePatt vpcIds - A list of Azure VNet IDs. - Leave empty for all VNets. + A list of AWS or GCP VPC IDs. + Leave empty for all VPCs. String[] @@ -5885,7 +5885,8 @@ $FQDNrules += New-SIAPolicyFQDNRuleDefinition -operator PREFIX -computernamePatt vnetIds - {{ Fill vnetIds Description }} + A list of Azure VNet IDs. + Leave empty for all VNets. String[] @@ -5963,8 +5964,8 @@ $FQDNrules += New-SIAPolicyFQDNRuleDefinition -operator PREFIX -computernamePatt vpcIds - A list of Azure VNet IDs. - Leave empty for all VNets. + A list of AWS or GCP VPC IDs. + Leave empty for all VPCs. String[] @@ -6212,7 +6213,8 @@ $FQDNrules += New-SIAPolicyFQDNRuleDefinition -operator PREFIX -computernamePatt vnetIds - {{ Fill vnetIds Description }} + A list of Azure VNet IDs. + Leave empty for all VNets. String[] @@ -6224,8 +6226,8 @@ $FQDNrules += New-SIAPolicyFQDNRuleDefinition -operator PREFIX -computernamePatt vpcIds - A list of Azure VNet IDs. - Leave empty for all VNets. + A list of AWS or GCP VPC IDs. + Leave empty for all VPCs. String[] diff --git a/README.md b/README.md index 892afe9..349cf5d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Logo][Logo] +![Logo][Logo] [Logo]: /docs/media/images/IdentityCommand.SIA.png @@ -6,16 +6,14 @@ **IdentityCommand.SIA** is a PowerShell module that provides a set of easy-to-use commands, allowing you to interact with the API for a **CyberArk Secure Infrastructure Access** from within the PowerShell environment. -| Main Branch | Latest Build | CodeFactor | Coverage | PowerShell Gallery | License | -| ------------------------ | ------------------------ | -------------------------- | ---------------------------- | ------------------------- | ---------------------------- | -| [![appveyor][]][av-site] | [![tests][]][tests-site] | [![codefactor][]][cf-site] | [![codecov][]][codecov-link] | [![psgallery][]][ps-site] | [![license][]][license-link] | +| Main Branch | CodeFactor | Coverage | PowerShell Gallery | License | +| ------------------------ | -------------------------- | ---------------------------- | ------------------------- | ---------------------------- | +| [![build][]][build-site] | [![codefactor][]][cf-site] | [![codecov][]][codecov-link] | [![psgallery][]][ps-site] | [![license][]][license-link] | -[appveyor]: https://ci.appveyor.com/api/projects/status/q2av77njofnsul92/branch/main?svg=true -[av-site]: https://ci.appveyor.com/project/pspete/IdentityCommand-SIA/branch/main +[build]: https://github.com/pspete/IdentityCommand.SIA/actions/workflows/ci.yml/badge.svg?branch=main&event=push +[build-site]: https://github.com/pspete/IdentityCommand.SIA/actions/workflows/ci.yml?query=branch%3Amain [psgallery]: https://img.shields.io/powershellgallery/v/IdentityCommand.SIA.svg [ps-site]: https://www.powershellgallery.com/packages/IdentityCommand.SIA -[tests]: https://img.shields.io/appveyor/tests/pspete/IdentityCommand-SIA.svg -[tests-site]: https://ci.appveyor.com/project/pspete/IdentityCommand-SIA [downloads]: https://img.shields.io/powershellgallery/dt/IdentityCommand.SIA.svg?color=blue [cf-site]: https://www.codefactor.io/repository/github/pspete/IdentityCommand.SIA [codefactor]: https://www.codefactor.io/repository/github/pspete/IdentityCommand.SIA/badge diff --git a/Tests/IdentityCommand.SIA.Tests.ps1 b/Tests/IdentityCommand.SIA.Tests.ps1 index 2044cc9..c914e07 100644 --- a/Tests/IdentityCommand.SIA.Tests.ps1 +++ b/Tests/IdentityCommand.SIA.Tests.ps1 @@ -1,227 +1,25 @@ -#Requires -Modules Pester, PSScriptAnalyzer +#Requires -Modules Pester <# .SYNOPSIS - Tests module for consistency, expected structures, settings, components & files. + Tests IdentityCommand.SIA-specific conventions across the module. .EXAMPLE Invoke-Pester .NOTES - A generic set of tests to apply to a module + The generic module tests come from pspete.Build (build/tests/Module.Tests.ps1). + The built module is a single psm1 with no Public folder, so Secure Value Handling finds no scripts to check against it. #> Describe 'Module' -Tag 'Consistency' { - #Get Current Directory - $Here = Split-Path -Parent $PSCommandPath + $ModuleName = 'IdentityCommand.SIA' - #Assume ModuleName from Repository Root folder - $ModuleName = (Split-Path (Split-Path $Here -Parent) -Leaf).Replace('-', '.') - - #Resolve Path to Module Directory - $ModulePath = Resolve-Path "$Here\..\$ModuleName" - - #Define Path to Module Manifest - $ManifestPath = Join-Path "$ModulePath" "$ModuleName.psd1" - - Get-Module -Name $ModuleName -All | Remove-Module -Force -ErrorAction Ignore - - $Module = Import-Module -Name "$ManifestPath" -ArgumentList $true -Force -ErrorAction Stop -PassThru - - #Get Public Function Names - $PublicFunctions = Get-ChildItem "$ModulePath\Public" -Include *.ps1 -Recurse | Select-Object -ExpandProperty BaseName - - #Get Exported Function Names - $ExportedFunctions = $Module.ExportedFunctions.Values.name - - $ExportedAliases = $Module.ExportedAliases.Values.name + $ModulePath = Join-Path (Split-Path (Split-Path -Parent $PSCommandPath) -Parent) $ModuleName $Scripts = Get-ChildItem $ModulePath -Include *.ps1 -Recurse - Context $ManifestPath -Tag Manifest { - - It 'has a valid manifest' -TestCases @{ManifestPath = $ManifestPath } { - param($ManifestPath) - { $null = Test-ModuleManifest -Path $ManifestPath -ErrorAction Stop -WarningAction SilentlyContinue } | - Should -Not -Throw - - } - - It 'specifies valid root module' -TestCases @{RootModule = $Module.RootModule ; ModuleName = $ModuleName } { - param($RootModule, $ModuleName) - $RootModule | Should -Be "$ModuleName.psm1" - - } - - It 'has a valid description' -TestCases @{Description = $Module.Description } { - param($Description) - $Description | Should -Not -BeNullOrEmpty - - } - - It 'has a valid guid' -TestCases @{Guid = $Module.Guid } { - param($Guid) - $Guid | Should -Be '634faae6-34c8-4887-a8fb-be9951c536f4' - - } - - It 'has a valid copyright' -TestCases @{Copyright = $Module.Copyright } { - param($Copyright) - $Copyright | Should -Not -BeNullOrEmpty - - } - - Context 'Files To Process' -Tag 'FilesToProcess' { - - foreach ($file in ($Module.ExportedFormatFiles)) { - Context $file -Tag 'FormatData' { - It 'exists' -TestCases @{ - 'File' = $file - } { - param($File) - $File | Should -Exist - } - - It 'is valid' -TestCases @{ - 'File' = $file - } { - param($File) - { Update-FormatData -AppendPath $File -ErrorAction Stop -WarningAction SilentlyContinue } | Should -Not -Throw - } - - } - - foreach ($file in ($Module.ExportedTypeFiles)) { - Context $file -Tag 'TypeData' { - It 'exists' -TestCases @{ - 'File' = $file - } { - param($File) - $File | Should -Exist - } - - It 'is valid' -TestCases @{ - 'File' = $file - } { - param($File) - { Update-TypeData -AppendPath $File -ErrorAction Stop -WarningAction SilentlyContinue } | Should -Not -Throw - } - - } - } - } - } - - Context 'Exported Function Analysis' -Tag 'Functions' { - - It 'exports the expected number of functions' { - - ($PublicFunctions | Measure-Object | Select-Object -ExpandProperty Count) | - - Should -Be ($ExportedFunctions | Measure-Object | Select-Object -ExpandProperty Count) - - } - - foreach ($ExportedFunction in $ExportedFunctions) { - - Context "$ExportedFunction" -Tag "$ExportedFunction" { - It 'is public' -TestCases @{ - 'ExportedFunction' = $ExportedFunction - 'PublicFunctions' = $PublicFunctions - } { - param($ExportedFunction, $PublicFunctions) - $PublicFunctions | Should -Contain $ExportedFunction - } - - It 'has a related pester tests file' -TestCases @{ - 'ExportedFunction' = $ExportedFunction - 'Here' = $here - } { - param($ExportedFunction, $here) - Test-Path (Join-Path $here "$ExportedFunction.Tests.ps1") | Should -Be $true - } + if ( -not (Get-Module -Name $ModuleName -All)) { - Context Help -Tag 'Help' { - - $help = Get-Help $ExportedFunction -Full - - It 'has synopsis' -TestCases @{ 'Help' = $help } { - param($help) - $help.synopsis | Should -Not -BeNullOrEmpty - - } - - It 'has description' -TestCases @{ 'Help' = $help } { - param($help) - $help.description | Should -Not -BeNullOrEmpty - - } - - It 'has example code' -TestCases @{ 'Help' = $help } { - param($help) - $help.examples.example.code | Should -Not -BeNullOrEmpty - - } - - [array]$HelpParameters = $help.parameters.parameter | Where-Object name -NotIn @('WhatIf', 'Confirm') - - foreach ($HelpParameter in $HelpParameters) { - - It 'has description of parameter ' -Tag "$($HelpParameter.name)" -TestCases @{ - 'description' = $HelpParameter.description - 'name' = $HelpParameter.name - } { - param($description, $name) - $description | Should -Not -BeNullOrEmpty - } - - } - - } - } - - } - - } - - Context 'Exported Alias Analysis' -Tag Alias { - - foreach ($Alias in $ExportedAliases) { - - It ' resolves to public function' -Tag $Alias -TestCases @{ - 'Alias' = $Alias - 'PublicFunctions' = $PublicFunctions - } { - param($Alias, $PublicFunctions) - $PublicFunctions | Should -Contain $((Get-Alias $Alias).ResolvedCommand.Name) - } - - } - - } - - } - - Context 'PSScriptAnalyzer Analysis' -Tag 'PSScriptAnalyzer' { - - Foreach ($Script in $scripts) { - - Context $Script.Name -Tag "$($Script.BaseName)", "$($Script.Name)" { - - It 'passes all Warning and Error rules' -TestCases @{ - 'FilePath' = $script.FullName - } { - param($FilePath) - - #One analyzer pass per file (all Warning/Error rules at once) rather than one pass per rule - $findings = Invoke-ScriptAnalyzer -Path $FilePath -Severity Warning, Error - - ($findings | ForEach-Object { "[$($_.RuleName)] line $($_.Line): $($_.Message)" }) -join [System.Environment]::NewLine | - Should -BeNullOrEmpty - - } - - } - - } + Import-Module -Name (Join-Path $ModulePath "$ModuleName.psd1") -ArgumentList $true -Force -ErrorAction Stop } @@ -263,8 +61,8 @@ Describe 'Module' -Tag 'Consistency' { Context 'Shared Helpers' { - #These live in IdentityCommand's Private folder and are loaded into this module's scope - #by the psm1. Asserting they resolve turns "the wrong IdentityCommand is loaded" into one + #These are IdentityCommand's private helpers, which the psm1 copies into this module's scope. + #Asserting they resolve turns "the wrong IdentityCommand is loaded" into one #clear failure rather than a cascade of unrelated ones. It 'resolves <_> from the loaded IdentityCommand module' -ForEach @( 'Add-QueryString' diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 26250a7..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,56 +0,0 @@ -# version format -version: 0.6.{build} - -environment: - #GIT_TRACE: 1 - # Plain (non-secret) config: - git_user_name: 'Pete Maan' - github_repo_owner: 'pspete' - - # Secure variables: - access_token: - secure: nXh9mZ0YD5M116yhStlR/Tzg+Kf+D6CUpNP+tIlWdNTVxNjVzT2vzl8wOD8kvtOiSORSWDsyvKZVx2+IsA0A1X05ohYwmkXQIF9TaPHiHtFmym8Y5btpRO/2AaWMfmIJ - psgallery_key: - secure: FuPgJskczZMptxRgdUlBAy7OYmXBQl4zq86kXXSmBt6wKudnM2PK7W6cM7bj0te1 - github_email: - secure: x5ljenzXfYXkzpEu9eX7AQvb3AkFbiXG2NndMzw9Zc4= - CODECOV_TOKEN: - secure: nUMw4tUP8DnGUwUMbXy8WrcW6YTBwS1td2BR1bHV9tE8MWE4XEYWDjPmUA/skGJp - -skip_tags: true - -skip_commits: - files: - - docs\* - - .github\* - - .vscode\* - - README.md - - LICENSE.md - - CONTRIBUTING.md - - CODE_OF_CONDUCT.md - - ISSUE_TEMPLATE.md - - PULL_REQUEST_TEMPLATE.md - - appveyor.yml - - CHANGELOG.md - message: /update readme.*|update version.*|update appveyor.*/ - -only_commits: - files: - - build\ - - IdentityCommand.SIA\ - - Tests\ - -image: Visual Studio 2019 - -install: - - pwsh.exe -File .\build\install.ps1 - -build_script: - - pwsh.exe -File .\build\build.ps1 - -test_script: - - pwsh.exe -File .\build\test.ps1 - -deploy_script: - - pwsh.exe -File .\build\deploy-github.ps1 - - pwsh.exe -File .\build\deploy-psgallery.ps1 \ No newline at end of file diff --git a/build/Import-IdentityCommand.ps1 b/build/Import-IdentityCommand.ps1 deleted file mode 100644 index ca1d400..0000000 --- a/build/Import-IdentityCommand.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -<# -.SYNOPSIS -Imports the IdentityCommand module the tests will run against. - -.DESCRIPTION -This module borrows its HTTP/auth plumbing from the private functions of the IdentityCommand -module, which the .psm1 locates via Get-Module. Which copy of IdentityCommand is loaded therefore -decides which helpers are available, so a refactor spanning both repositories has to be testable -against a working-tree IdentityCommand rather than the published one. - -Resolution order is -Path, then $env:IDENTITYCOMMAND_MODULE_PATH, then the installed module. With -neither supplied the behaviour is identical to Import-Module IdentityCommand -Force, so CI is -unaffected. - -Any already loaded copy is removed first: the .psm1 resolves a single module, and leaving two -versions loaded makes which one it picks depend on load order. - -.PARAMETER Path -Path to an IdentityCommand.psd1, or to the directory containing it. - -.EXAMPLE -.\build\Import-IdentityCommand.ps1 - -Imports the installed IdentityCommand module. - -.EXAMPLE -.\build\Import-IdentityCommand.ps1 -Path ..\IdentityCommand\IdentityCommand - -Imports IdentityCommand from a local checkout. -#> -[CmdletBinding()] -param( - - [parameter(Mandatory = $false, Position = 0)] - [string]$Path = $env:IDENTITYCOMMAND_MODULE_PATH - -) - -Get-Module -Name IdentityCommand -All | Remove-Module -Force -ErrorAction SilentlyContinue - -if (-not [string]::IsNullOrWhiteSpace($Path)) { - - if (Test-Path -Path $Path -PathType Container) { - $Path = Join-Path $Path 'IdentityCommand.psd1' - } - - if (-not (Test-Path -Path $Path -PathType Leaf)) { - throw "IdentityCommand manifest not found at '$Path'" - } - - Import-Module -Name $Path -Force -ErrorAction Stop - -} else { - - Import-Module -Name IdentityCommand -Force -ErrorAction Stop - -} - -$Loaded = @(Get-Module -Name IdentityCommand) - -if ($Loaded.Count -ne 1) { - throw "Expected a single IdentityCommand module to be loaded, found $($Loaded.Count)" -} - -Write-Host "Using IdentityCommand $($Loaded[0].Version) from $($Loaded[0].ModuleBase)" -ForegroundColor Cyan diff --git a/build/build.ps1 b/build/build.ps1 deleted file mode 100644 index 26518fc..0000000 --- a/build/build.ps1 +++ /dev/null @@ -1,102 +0,0 @@ -#---------------------------------# -# Header # -#---------------------------------# -Write-Host 'Build Information:' -ForegroundColor Yellow - -#Get current module version from manifest -$ManifestPath = Join-Path "$pwd" $(Join-Path "$env:APPVEYOR_PROJECT_NAME" "$env:APPVEYOR_PROJECT_NAME.psd1") -$CurrentVersion = (Import-PowerShellDataFile $ManifestPath).ModuleVersion - -#display module information -Write-Host "ModuleName : $env:APPVEYOR_PROJECT_NAME" -Write-Host "Build version : $env:APPVEYOR_BUILD_VERSION" -Write-Host "Manifest version : $CurrentVersion" -Write-Host "Author : $env:APPVEYOR_REPO_COMMIT_AUTHOR" -Write-Host "Branch : $env:APPVEYOR_REPO_BRANCH" -Write-Host "Build Folder : $env:APPVEYOR_BUILD_FOLDER" - -If ([System.Version]$($env:APPVEYOR_BUILD_VERSION) -le [System.Version]$CurrentVersion) { - - throw "Build Version Not Greater than Current Version" - -} -Else { - - Try { - - #---------------------------------# - # BuildScript # - #---------------------------------# - #---------------------------------# - # Update module manifest # - #---------------------------------# - Write-Host "Updating Manifest Version to $env:APPVEYOR_BUILD_VERSION" -ForegroundColor Cyan - - #Replace version in manifest with build version from appveyor - ((Get-Content $ManifestPath).replace("= '$($currentVersion)'", "= '$($env:APPVEYOR_BUILD_VERSION)'")) | Set-Content $ManifestPath -ErrorAction Stop - - <#-- Package Version Release --#> - $Directory = New-Item -ItemType Directory -Path "Release\$($env:APPVEYOR_PROJECT_NAME)\$($env:APPVEYOR_BUILD_VERSION)" -Force -ErrorAction Stop - $OutputArchive = "$($env:APPVEYOR_PROJECT_NAME)-v$($env:APPVEYOR_BUILD_VERSION).zip" - - $OutputArchive = "$($env:APPVEYOR_PROJECT_NAME)-v$($env:APPVEYOR_BUILD_VERSION).zip" - $ReleaseSource = $(Resolve-Path .\$env:APPVEYOR_PROJECT_NAME) - - <#-- Create Release Folder --#> - $Directory = New-Item -ItemType Directory -Path "..\Release\$($env:APPVEYOR_PROJECT_NAME)\$($env:APPVEYOR_BUILD_VERSION)" -Force -ErrorAction Stop - - <#-- Copy Module Files --#> - Copy-Item -Path $ReleaseSource\* -Recurse -Destination $($Directory.Fullname) -Force -ErrorAction Stop - - - If ((-not ($ENV:APPVEYOR_PULL_REQUEST_NUMBER)) -and (($ENV:APPVEYOR_REPO_BRANCH -eq 'main') -and ([version]$ENV:APPVEYOR_BUILD_VERSION -ge [version]"1.0.0"))) { - - If (($ENV:sig_key) -and ($ENV:PfxSecure)) { - - Write-Host "Signing Files" -ForegroundColor Cyan - - Try { - $KeyPath = Join-Path $([System.Environment]::GetEnvironmentVariable("TEMP")) cert.pfx - [IO.File]::WriteAllBytes($KeyPath, [Convert]::FromBase64String($($env:sig_key))) - - $SecurePW = ConvertTo-SecureString -String $($env:PfxSecure) -Force -AsPlainText - $Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList "UserName", $SecurePW - - $Cert = Get-ChildItem -Path $KeyPath | Import-PfxCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Password $Cred.Password - - $null = Get-ChildItem -Path "$($Directory.Fullname)\*.ps*" -Recurse | Set-AuthenticodeSignature -Certificate $Cert -TimestampServer 'http://timestamp.digicert.com' - $null = New-FileCatalog -CatalogVersion 2 -CatalogFilePath "$($Directory.Fullname)\$($env:APPVEYOR_PROJECT_NAME).cat" -Path $($Directory.Fullname) - $null = Set-AuthenticodeSignature -Certificate $Cert -TimestampServer 'http://timestamp.digicert.com' -FilePath "$($Directory.Fullname)\$($env:APPVEYOR_PROJECT_NAME).cat" - } - Catch { - Throw $_ - } - Finally { - - Get-ChildItem -Path "Cert:\CurrentUser\My" -Recurse -CodeSigningCert | Remove-Item -Force - Remove-Item -Path $KeyPath -Force - Remove-Variable -Name SecurePW -Force - Remove-Variable -Name Cred -Force - - } - - } - - } - - <#-- Create Package ---#> - Compress-Archive $Directory -DestinationPath ..\$OutputArchive -ErrorAction Stop - - <#-- Release Artifact --#> - Write-Host "Release Artifact : $OutputArchive" - Push-AppveyorArtifact ..\$OutputArchive -FileName $OutputArchive -DeploymentName "$env:APPVEYOR_PROJECT_NAME-latest" - - } - - Catch { - - throw $_ - - } - -} \ No newline at end of file diff --git a/build/deploy-github.ps1 b/build/deploy-github.ps1 deleted file mode 100644 index 7ada822..0000000 --- a/build/deploy-github.ps1 +++ /dev/null @@ -1,120 +0,0 @@ -<#--------------------------------- -Update version number on GitHub to match build version - -Requires these plain (non-secret) environment variables to be set in appveyor.yml: - git_user_name - e.g. 'Pete Maan' - github_repo_owner - e.g. 'pspete' -(github_email and access_token remain secure variables) ----------------------------------#> - - -if (-not ($ENV:APPVEYOR_PULL_REQUEST_NUMBER)) { - - If ($ENV:APPVEYOR_REPO_BRANCH -eq 'main') { - - <# Master Branch #> - Write-Host 'Deploy Process: GitHub Repository' -ForegroundColor Yellow - - <#---------------------------------#> - <# If Not a PR #> - <# Push psd1 file to ORIGIN Branch #> - <#---------------------------------#> - - Try { - - Write-Host "Push Updated $($env:APPVEYOR_PROJECT_NAME).psd1 to GitHub..." -ForegroundColor Yellow - - git config --global core.safecrlf false - git config --global credential.helper store - Add-Content "$HOME\.git-credentials" "https://$($env:access_token):x-oauth-basic@github.com`n" - git config --global user.email "$($env:github_email)" - git config --global user.name "$($env:git_user_name)" - - git checkout -q $($ENV:APPVEYOR_REPO_BRANCH) - - git add $(Join-Path -Path (Join-Path -Path "$env:APPVEYOR_BUILD_FOLDER" -ChildPath "$env:APPVEYOR_PROJECT_NAME") -ChildPath "$env:APPVEYOR_PROJECT_NAME.psd1") - - git status - - git commit -s -m "Update Version: $($env:APPVEYOR_BUILD_VERSION)" - - git push --porcelain origin $($ENV:APPVEYOR_REPO_BRANCH) - - Write-Host "$($env:APPVEYOR_PROJECT_NAME) version $($env:APPVEYOR_BUILD_VERSION) pushed to GitHub." -ForegroundColor Cyan - - } - - Catch { - - Write-Host 'Push to GitHub failed.' -ForegroundColor Red - throw $_ - - } - - Write-Host 'Deploy Process: GitHub Release' -ForegroundColor Yellow - - If ([version]$env:APPVEYOR_BUILD_VERSION -ge [version]'0.1.0') { - - <# Create New Release #> - - $token = $env:access_token - $uploadFilePath = Resolve-Path "..\$($env:APPVEYOR_PROJECT_NAME)-v$($env:APPVEYOR_BUILD_VERSION).zip" - $releaseName = "v$($env:APPVEYOR_BUILD_VERSION)" - $repo = "$($env:github_repo_owner)/$env:APPVEYOR_PROJECT_NAME" - - $headers = @{ - 'Authorization' = "token $token" - 'Content-type' = 'application/json' - } - - $body = @{ - tag_name = $releaseName - name = $releaseName - body = "$($env:APPVEYOR_PROJECT_NAME) v$($env:APPVEYOR_BUILD_VERSION)" - draft = $false - prerelease = $false - } - - Write-Host "Creating release $releaseName..." -NoNewline - - try { - $json = (ConvertTo-Json $body) - $release = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/releases" -Headers $headers -Method POST -Body $json - $uploadUrl = $release.upload_url.Replace('{?name,label}', '') + '?name=' + [IO.Path]::GetFileName($uploadFilePath) - Write-Host 'OK' -ForegroundColor Green - - Write-Host "Uploading asset $($env:APPVEYOR_PROJECT_NAME)-v$($env:APPVEYOR_BUILD_VERSION).zip..." -NoNewline - - $data = [System.IO.File]::ReadAllBytes($uploadFilePath) - $wc = New-Object Net.WebClient - $wc.Headers['Content-type'] = 'application/octet-stream' - $wc.Headers['Authorization'] = "token $token" - - $null = $wc.UploadData($uploadUrl, 'POST', $data) - Write-Host 'OK' -ForegroundColor Green - } catch { - - Write-Host 'GitHub Release Failed.' -ForegroundColor Red - throw $_ - - } - - } - - } - - Else { - - <# Not Master Branch #> - Write-Host "$ENV:APPVEYOR_REPO_BRANCH Branch; No Release" -ForegroundColor Cyan - exit - - } - -} - -Else { - - Write-Host 'Skipping Deploy Process: GitHub Repository' -ForegroundColor Yellow - -} \ No newline at end of file diff --git a/build/deploy-psgallery.ps1 b/build/deploy-psgallery.ps1 deleted file mode 100644 index 5a3f458..0000000 --- a/build/deploy-psgallery.ps1 +++ /dev/null @@ -1,68 +0,0 @@ -<#--------------------------------- -Auto-publish changes to master branch as a new module version in the PSGallery. -- Only publish if build version is greater than 1.0 -- Skip Auto-publish with specific commit message of "Manual Deployment" ----------------------------------#> - - -if (-not ($ENV:APPVEYOR_PULL_REQUEST_NUMBER)) { - - <#---------------------------------#> - <# If Not a PR #> - <#---------------------------------#> - If (($ENV:APPVEYOR_REPO_BRANCH -eq 'main') -and ([version]$env:APPVEYOR_BUILD_VERSION -ge [version]'0.1.0')) { - - <# Master Branch #> - <# Version 0.1.0+ #> - - Write-Host 'Deploy Process: PowerShell Gallery' -ForegroundColor Yellow - - If ($ENV:APPVEYOR_REPO_COMMIT_MESSAGE -eq 'Manual Deployment') { - - <# Manual Deploy to PSGallery #> - Write-Host "Finished testing of branch: $env:APPVEYOR_REPO_BRANCH" -ForegroundColor Cyan - Write-Host 'Manual Deployment to PSGallery Required' -ForegroundColor Cyan - Write-Host 'Exiting' -ForegroundColor Cyan - exit - - } Else { - - <#---------------------------------# - # Publish to PS Gallery # - #----------------------------------#> - - $ModulePath = Resolve-Path "..\Release\$($env:APPVEYOR_PROJECT_NAME)\$($env:APPVEYOR_BUILD_VERSION)" - - Write-Host "Publish $($env:APPVEYOR_PROJECT_NAME) $($env:APPVEYOR_BUILD_VERSION) to Powershell Gallery......" -NoNewline - - Try { - - Publish-Module -Path $ModulePath -NuGetApiKey $($env:psgallery_key) -SkipAutomaticTags -Confirm:$false -ErrorAction Stop -Force - - Write-Host 'OK' -ForegroundColor Green - - } Catch { - - Write-Host "Failed - $_." -ForegroundColor Red - throw $_ - - } Finally { - exit - } - - } - - } Else { - - <# No Deployment #> - - Write-Host "Finished testing: $($env:APPVEYOR_PROJECT_NAME) $env:APPVEYOR_REPO_BRANCH ($($env:APPVEYOR_BUILD_VERSION)) - Exiting" -ForegroundColor Cyan - exit - - } - -} Else { - - Write-Host 'Skipping Deploy Process: PowerShell Gallery' -ForegroundColor Yellow - -} \ No newline at end of file diff --git a/build/install.ps1 b/build/install.ps1 deleted file mode 100644 index 0090c1e..0000000 --- a/build/install.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -#---------------------------------# -# Header # -#---------------------------------# -Write-Host 'Installing Required Modules:' -ForegroundColor Yellow - -$RequiredModules = @( - @{ Name = 'Pester'; RequiredVersion = '5.7.1' } - @{ Name = 'PSScriptAnalyzer' } - @{ Name = 'IdentityCommand' } -) - -#---------------------------------# -# Install NuGet # -#---------------------------------# -if (-not $IsCoreCLR) { - Write-Host "`tNuGet..." - $pkg = Install-PackageProvider -Name NuGet -Confirm:$false -Force -ErrorAction Stop - Write-Host "`t`tInstalled NuGet version '$($pkg.version)'" -} -#---------------------------------# -# Install Required Modules # -#---------------------------------# -foreach ($Module in $RequiredModules) { - - try { - Write-Host "`tInstalling: $($Module.Name)..." -NoNewline - $InstallParams = @{ - Name = $Module.Name - Repository = 'PSGallery' - Confirm = $false - Force = $true - SkipPublisherCheck = $true - ErrorAction = 'Stop' - } - if ($Module.RequiredVersion) { - $InstallParams.RequiredVersion = $Module.RequiredVersion - } - Install-Module @InstallParams | Out-Null - Write-Host ' OK' -ForegroundColor Green - } catch { - Write-Host 'Error' -ForegroundColor Red - throw $_ - } - -} \ No newline at end of file diff --git a/build/test.ps1 b/build/test.ps1 deleted file mode 100644 index 92ca52c..0000000 --- a/build/test.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -#---------------------------------# -# Header # -#---------------------------------# -Write-Host "Testing: PSVersion $($PSVersionTable.PSVersion)" -ForegroundColor Yellow -$ManifestPath = Join-Path "$pwd" $(Join-Path "$env:APPVEYOR_PROJECT_NAME" "$env:APPVEYOR_PROJECT_NAME.psd1") -Import-Module Pester -RequiredVersion 5.7.1 -Force -& (Join-Path $PSScriptRoot 'Import-IdentityCommand.ps1') -Import-Module $ManifestPath -ArgumentList $true -Force -#---------------------------------# -# Run Pester Tests # -#---------------------------------# -$files = Get-ChildItem $(Join-Path $ENV:APPVEYOR_BUILD_FOLDER $env:APPVEYOR_PROJECT_NAME) -Include *.ps1 -Recurse | Select-Object -ExpandProperty FullName - -# get default from static property -$configuration = [PesterConfiguration]::Default -# assing properties & discover via intellisense -$configuration.Run.Path = '.\Tests' -$configuration.Run.PassThru = $true -$configuration.CodeCoverage.Enabled = $true -$configuration.CodeCoverage.Path = $files -$configuration.CodeCoverage.OutputFormat = 'JaCoCo' -$configuration.CodeCoverage.OutputPath = '.\coverage.xml' -$configuration.TestResult.Enabled = $true -$configuration.TestResult.OutputFormat = 'JUnitXml' -$configuration.TestResult.OutputPath = '.\TestResults.xml' -$configuration.Output.Verbosity = 'Minimal' - -$result = Invoke-Pester -Configuration $configuration - -$res = $result | ConvertTo-Pester4Result - -Write-Host 'Uploading Test Results' -$null = (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", $(Resolve-Path .\TestResults.xml)) - -if (($env:APPVEYOR_REPO_COMMIT_AUTHOR -eq $env:git_user_name) -and -not [string]::IsNullOrWhiteSpace($env:CODECOV_TOKEN)) { - - #CODECOV_TOKEN is a secure variable and is not exposed to pull request builds - skip the upload rather than - #invoking the CLI with an empty -t value (which shifts every following argument and fails the command). - - Write-Host 'Publishing Code Coverage' - - try { - $ProgressPreference = 'SilentlyContinue' - $null = Invoke-WebRequest -Uri 'https://cli.codecov.io/latest/windows/codecov.exe' -OutFile codecov.exe - .\codecov.exe --disable-telem upload-process --disable-search --fail-on-error -t "$env:CODECOV_TOKEN" -n 'appveyor' -f coverage.xml - .\codecov.exe --disable-telem do-upload --disable-search --fail-on-error -t "$env:CODECOV_TOKEN" --report-type test_results -f TestResults.xml - } catch { - Write-Warning "Code coverage upload failed: $_" - } - - Remove-Item -Path .\codecov.exe -Force -ErrorAction SilentlyContinue - -} - -Remove-Item -Path $(Resolve-Path .\coverage.xml) -Force -Remove-Item -Path $(Resolve-Path .\TestResults.xml) -Force - -#---------------------------------# -# Validate # -#---------------------------------# -if (($res.FailedCount -gt 0) -or ($res.PassedCount -eq 0)) { - - throw "$($res.FailedCount) tests failed." - -} else { - - Write-Host 'All tests passed' -ForegroundColor Green - -} \ No newline at end of file diff --git a/docs/collections/_commands/New-SIAPolicyProviderDefinition.md b/docs/collections/_commands/New-SIAPolicyProviderDefinition.md index 2947447..29bd520 100644 --- a/docs/collections/_commands/New-SIAPolicyProviderDefinition.md +++ b/docs/collections/_commands/New-SIAPolicyProviderDefinition.md @@ -330,7 +330,9 @@ Accept wildcard characters: False ``` ### -vnetIds -{{ Fill vnetIds Description }} +A list of Azure VNet IDs. + +Leave empty for all VNets. ```yaml Type: String[] @@ -345,9 +347,9 @@ Accept wildcard characters: False ``` ### -vpcIds -A list of Azure VNet IDs. +A list of AWS or GCP VPC IDs. -Leave empty for all VNets. +Leave empty for all VPCs. ```yaml Type: String[] diff --git a/docs/collections/_posts/2024-03-03-identitycommand.sia-release-0-1.md b/docs/collections/_posts/2024-03-03-identitycommand.sia-release-0-1.md new file mode 100644 index 0000000..80309be --- /dev/null +++ b/docs/collections/_posts/2024-03-03-identitycommand.sia-release-0-1.md @@ -0,0 +1,45 @@ +--- +title: "IdentityCommand.SIA Release 0.1" +date: 2024-03-03 00:00:00 +version: 0.1.10 +tags: + - Release Notes +--- + +## [0.1.10] + +### Changed + +- Published to PowerShell Gallery + +## [0.1.9] + +Initial release of `IdentityCommand.DPA` module + +### Added + +- `Add-DPATargetSet` +- `Connect-DPATenant` +- `Connect-DPATarget` +- `Get-DPACertificate` +- `Get-DPAConnector` +- `Get-DPAConnectorSetupScript` +- `Get-DPAPolicy` +- `Get-DPAModuleData` +- `Get-DPASetting` +- `Get-DPASession` +- `Get-DPASSHPublicKey` +- `Get-DPAStrongAccount` +- `Get-DPATargetSet` +- `New-DPAPolicy` +- `New-DPAPolicyConnectAsDefinition` +- `New-DPAPolicyFQDNRuleDefinition` +- `New-DPAPolicyProviderDefinition` +- `New-DPAPolicyUserAccessRuleDefinition` +- `New-DPAPolicyUserDataDefinition` +- `New-DPAStrongAccount` +- `Remove-DPAPolicy` +- `Remove-DPAStrongAccount` +- `Remove-DPATargetSet` +- `Set-DPAPolicy` +- `Set-DPASetting` diff --git a/docs/collections/_posts/2024-03-05-identitycommand.sia-release-0-2.md b/docs/collections/_posts/2024-03-05-identitycommand.sia-release-0-2.md new file mode 100644 index 0000000..eacd4a9 --- /dev/null +++ b/docs/collections/_posts/2024-03-05-identitycommand.sia-release-0-2.md @@ -0,0 +1,15 @@ +--- +title: "IdentityCommand.SIA Release 0.2" +date: 2024-03-05 00:00:00 +version: 0.2.11 +tags: + - Release Notes +--- + +## [0.2.11] + +### Changed + +- `Connect-DPATarget` + - Updated to save RDP file and automatically invoke rdp connection via DPA + - Updated to calculate ssh connection string and automatically invoke ssh connection diff --git a/docs/collections/_posts/2026-09-01-identitycommand.sia-release-0-3.md b/docs/collections/_posts/2026-09-01-identitycommand.sia-release-0-3.md new file mode 100644 index 0000000..7f0af6c --- /dev/null +++ b/docs/collections/_posts/2026-09-01-identitycommand.sia-release-0-3.md @@ -0,0 +1,94 @@ +--- +title: "IdentityCommand.SIA Release 0.3" +date: 2026-09-01 00:00:00 +version: 0.3.32 +tags: + - Release Notes + - New-SIAStrongAccount + - Set-SIAStrongAccount + - New-SIADatabaseStrongAccount + - Set-SIADatabaseStrongAccount + - Get-SIASSHHostKeyFingerprint + - Add-SIASSHHostKeyFingerprint + - Set-SIASSHHostKeyFingerprint + - Remove-SIASSHHostKeyFingerprint + - Invoke-SIASSHPublicKeyRotation + - Get-SIAMFAKey + - Remove-SIAConnector + - Test-SIAConnector + - Update-SIAConnector + - Set-SIAConnectorMaintenanceMode + - Add-SIAConnectorPoolMember + - Invoke-SIAConnectorCertificateRotation + - Get-SIAHttpsRelay + - Remove-SIAHttpsRelay + - Update-SIAHttpsRelay + - Get-SIAHttpsRelaySetupScript + - Invoke-SIAHttpsRelayCertificateRotation + - Get-SIADatabaseStrongAccount + - Remove-SIADatabaseStrongAccount + - Get-SIADatabaseTarget + - Get-SIAPolicy + - Get-SIAStrongAccount + - Remove-SIAStrongAccount + - Add-SIATargetSet + - Get-SIATargetSet + - Remove-SIATargetSet + - Get-SIAConnectorSetupScript + - Get-SIASetting + - Set-SIASetting + - Get-SIACertificate + - Get-SIAResource +--- + +## [0.3.32] + +### Security + +- Secret-bearing request bodies (`New-SIAStrongAccount`, `Set-SIAStrongAccount`, `New-SIADatabaseStrongAccount`, `Set-SIADatabaseStrongAccount`) are now sent to `Invoke-IDRestMethod` as UTF8 bytes instead of a JSON string, so the plaintext password / secret cannot be captured by Windows PowerShell ParameterBinding / Module Logging. Mirrors [pspete/psPAS#602](https://github.com/pspete/psPAS/issues/602) / [pspete/psPAS#627](https://github.com/pspete/psPAS/pull/627). A static regression guard test was added to `IdentityCommand.SIA.Tests.ps1`. + +### Added + +- `Get-SIASSHHostKeyFingerprint`, `Add-SIASSHHostKeyFingerprint`, `Set-SIASSHHostKeyFingerprint`, `Remove-SIASSHHostKeyFingerprint` + - Manage SSH host key fingerprints for targets. +- `Invoke-SIASSHPublicKeyRotation` + - Generate a new SSH CA public key version, or deactivate / reactivate the previous version. +- `Get-SIAMFAKey` + - Retrieve the SIA MFA key (openssh or ppk format) used for SSH authentication, returned as text. +- `Remove-SIAConnector`, `Test-SIAConnector`, `Update-SIAConnector`, `Set-SIAConnectorMaintenanceMode`, `Add-SIAConnectorPoolMember`, `Invoke-SIAConnectorCertificateRotation` + - Delete, test reachability of, upgrade, set maintenance mode on, assign to pools, and rotate the certificate of connectors. +- `Get-SIAHttpsRelay`, `Remove-SIAHttpsRelay`, `Update-SIAHttpsRelay`, `Get-SIAHttpsRelaySetupScript`, `Invoke-SIAHttpsRelayCertificateRotation` + - Manage SIA HTTPS relays, generate relay installation scripts, and rotate relay certificates. +- `Set-SIAStrongAccount` + - Update an existing virtual machine strong account. +- `Get-SIADatabaseStrongAccount`, `New-SIADatabaseStrongAccount`, `Set-SIADatabaseStrongAccount`, `Remove-SIADatabaseStrongAccount` + - Manage database strong accounts via the `/api/database-strong-accounts` API (`store_type` / `account_properties` / `password_secret_object` body), supporting PAM accounts and managed accounts for PostgreSQL, MySQL, MariaDB, MSSql, Oracle, MongoDB, DB2UnixSSH, WinDomain and AWSAccessKeys platforms. +- `Get-SIADatabaseTarget` + - Lists the database targets configured in SIA (`GET /api/database-targets`). + +### Changed + +- Module renamed from `IdentityCommand.DPA` to `IdentityCommand.SIA`, reflecting the rebrand of CyberArk Dynamic Privileged Access to CyberArk Secure Infrastructure Access. + - All commands renamed to use the `SIA` noun prefix in place of `DPA` (e.g. `Get-DPAPolicy` is now `Get-SIAPolicy`). + - Repository, folder structure, and help content updated to match. +- `Get-SIAStrongAccount`, `New-SIAStrongAccount`, `Remove-SIAStrongAccount` **(breaking)** + - The `-database` / `-databases` switches have been removed - database strong accounts are now managed with the dedicated `*-SIADatabaseStrongAccount` commands. These commands now only manage virtual machine strong accounts (`/api/secrets`). + - `New-SIAStrongAccount` parameter sets renamed `StoredInDPA-VM` -> `StoredInSIA` and `VaultedInPrivilegeCloud-VM` -> `VaultedInPrivilegeCloud`. + - `New-SIAStrongAccount` / `Set-SIAStrongAccount` gained optional `-enable_bulk_elevation` and `-ephemeral_domain_user_data` parameters. + - `Get-SIAStrongAccount` gained `-count` and `-offset` list parameters. +- `Add-SIATargetSet` / `Get-SIATargetSet` / `Remove-SIATargetSet` + - Target set endpoints moved from `/api/discovery/targetsets` to `/api/targetsets`. +- `Get-SIAConnectorSetupScript` **(breaking)** + - Removed the `-connector_type` parameter - it is not part of the `POST /api/connectors/setup-script` request body. + - Added optional `-expiration_minutes` (15 - 240), `-proxy_host`, `-proxy_port` and `-windows_installation_path` parameters. +- `Get-SIASetting` / `Set-SIASetting` + - `-FeatureName` (Get) and the feature switches (Set) now cover all settings features exposed by SIA, including `rdpTokenMfaCaching`, `rdpTranscription`, `sshRecording`, `logonSequence`, `selfHostedPam`, `connectViaBrowser`, `rdpFileSigning`, `rdpKerberosAuthMode`, `rdpChannels`, `validateFingerprintForSshZeroStanding`, `httpsRelay`, `rdpFileParameters`, `granularEnabled`, `oracleOud` and `oracleConnectionProtocol`. + - `Set-SIASetting` now performs a true partial update - only the supplied sub-settings are sent (`PATCH /api/settings/`), instead of reading and re-sending the full configuration. + - `Set-SIASetting` validates `-keyExpirationTimeSec` (300 - 43200 seconds), `-sessionMaxDuration` (60 - 1440 minutes), `-sessionIdleTime` (1 - 120 minutes), `-logonSequenceValue` (up to 30000 characters) and `-shellPromptForAudit` (up to 1024 characters). + +### Fixed + +- `Get-SIACertificate` + - Updated to return correct property of output value. +- `Get-SIAResource` + - Updated to return correct property of output value. diff --git a/docs/collections/_posts/2026-09-05-identitycommand.sia-release-0-4.md b/docs/collections/_posts/2026-09-05-identitycommand.sia-release-0-4.md new file mode 100644 index 0000000..e3a9d32 --- /dev/null +++ b/docs/collections/_posts/2026-09-05-identitycommand.sia-release-0-4.md @@ -0,0 +1,34 @@ +--- +title: "IdentityCommand.SIA Release 0.4" +date: 2026-09-05 00:00:00 +version: 0.4.50 +tags: + - Release Notes + - Get-SIAPolicy + - Get-SIASession + - Get-SIAStrongAccount + - Get-SIATargetSet + - Get-SIADatabaseStrongAccount + - Get-SIADatabaseTarget + - Add-SIATargetSet + - Connect-SIATenant + - Set-SIAPolicy + - Remove-SIAPolicy +--- + +## [0.4.50] + +### Added + +- Automatic result pagination for `Get-SIAPolicy`, `Get-SIASession`, `Get-SIAStrongAccount`, `Get-SIATargetSet`, `Get-SIADatabaseStrongAccount` and `Get-SIADatabaseTarget` - these now transparently fetch every page instead of only the first. +- Tab-completion for id/name parameters across connector, policy, strong-account, target-set and HTTPS-relay commands. +- `Get-SIAPolicy` gained `-limit` and `-sort` list parameters. +- `Add-SIATargetSet` now supports `-WhatIf` / `-Confirm`. +- `Connect-SIATenant`: when no `IdentityCommand` session exists, authenticates to CyberArk Identity itself via new `-Credential` (interactive `New-IDSession`), `-Credential -PlatformToken` (service user `New-IDPlatformToken`) or `-SAMLResponse` parameters. The Identity url is discovered from the given subdomain / SIA url. Existing-session behaviour is unchanged. Also gained `-WhatIf` / `-Confirm`. + +### Changed + +- `Get-SIAStrongAccount` **(breaking)**: `-offset` removed - the command now pages results internally. +- `Get-SIATargetSet` **(breaking)**: `-b64StartKey` removed - the command now pages results internally. +- `Get-SIADatabaseStrongAccount`, `Get-SIADatabaseTarget` **(breaking)**: `-cursor` removed - the commands now page results internally. +- `Set-SIAPolicy`: `-policyId` parameter renamed `-policyid` for consistency with `Get-SIAPolicy` / `Remove-SIAPolicy`. diff --git a/docs/collections/_posts/2026-09-07-identitycommand.sia-release-0-5.md b/docs/collections/_posts/2026-09-07-identitycommand.sia-release-0-5.md new file mode 100644 index 0000000..e7a44ba --- /dev/null +++ b/docs/collections/_posts/2026-09-07-identitycommand.sia-release-0-5.md @@ -0,0 +1,22 @@ +--- +title: "IdentityCommand.SIA Release 0.5" +date: 2026-09-07 00:00:00 +version: 0.5.60 +tags: + - Release Notes + - Connect-SIATenant +--- + +## [0.5.60] + +### Changed + +- The module's private helpers are now the shared implementations provided by `IdentityCommand`, rather than SIA-specific copies. `ConvertTo-SIAJsonBody`, `ConvertTo-SIASecretBody`, `Get-SIAPagedResult`, `Merge-SIAParameter`, `Resolve-SIAServiceUrl`, `Get-SIACompletionResult` and `Get-SIAArgumentCompleter` have been removed in favour of `ConvertTo-JsonBody`, `ConvertTo-SecretBody`, `Get-PagedResult`, `Merge-Parameter`, `Resolve-ServiceUrl`, `Get-CompletionResult` and `Get-ArgumentCompleter`. Query strings are now built with the shared `Add-QueryString` in place of hand-assembled `ConvertTo-QueryString` output. No change to command behaviour or output. + - `RequiredModules` raised accordingly: `IdentityCommand` `0.6.158` or later is now required. +- `Connect-SIATenant` **(breaking)**: supplying `-Credential` (with or without `-PlatformToken`) or `-SAMLResponse` now always authenticates to CyberArk Identity and replaces any existing `IdentityCommand` session. Previously an active session took precedence and the supplied authentication parameters were ignored. Calls that supply no authentication parameters are unchanged - an existing session is still used as-is, and its absence is still an error. + +### Fixed + +- `IdentityCommand`'s private helpers are now loaded before this module's own files, so the argument completer registrations that run at import time can call them. Previously they were loaded afterwards. +- The module loader resolves a single `IdentityCommand` module - the highest version - when more than one version is loaded. Previously every loaded version's `Private` folder was dot-sourced, last one winning. +- Importing `IdentityCommand.SIA` without `IdentityCommand` loaded now throws a clear error instead of failing obscurely. diff --git a/docs/collections/_posts/2026-10-07-identitycommand.sia-release-0-6.md b/docs/collections/_posts/2026-10-07-identitycommand.sia-release-0-6.md new file mode 100644 index 0000000..582ed3c --- /dev/null +++ b/docs/collections/_posts/2026-10-07-identitycommand.sia-release-0-6.md @@ -0,0 +1,21 @@ +--- +title: "IdentityCommand.SIA Release 0.6" +date: 2026-10-07 00:00:00 +version: 0.6.67 +tags: + - Release Notes + - Get-SIAVirtualMachine + - Remove-SIAVirtualMachine +--- + +## [0.6.67] + +### Added + +- `Get-SIAVirtualMachine`: get details of VM infrastructure targets, with `-filter`, `-source`, + `-sort` and `-search`. Results are paginated automatically. +- `Remove-SIAVirtualMachine`: delete a VM infrastructure target by machine ID. + +### Changed + +- The module loader copies `IdentityCommand`'s private helper functions from the loaded module's session state instead of dot-sourcing its `Private` folder, so it works with both the current `IdentityCommand` layout and the combined single-file layout of future releases. Each copied helper runs in this module's scope and uses its session.