Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c7ce00504 | |||
| 14c8c4f384 | |||
| 5e5145fdc7 | |||
| 6318d06362 | |||
| 98f5d7704e | |||
| 94bd15a8f8 | |||
| daf1cdce65 | |||
| 80871b73af | |||
| 9a2f81fc02 | |||
| 97193d46f2 | |||
| b5575222eb | |||
| f82312433c | |||
| 1aa51b8cbf | |||
| 77cb03ec98 |
@@ -0,0 +1,328 @@
|
|||||||
|
name: Publish to PowerShell Gallery
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: github.event.pull_request.merged == true
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Verify host prerequisites (pwsh, dotnet)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$missing = @()
|
||||||
|
if (-not (Get-Command pwsh -ErrorAction SilentlyContinue)) { $missing += 'pwsh' }
|
||||||
|
if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) { $missing += 'dotnet' }
|
||||||
|
if ($missing.Count -gt 0) {
|
||||||
|
throw "Host runner is missing required tool(s): $($missing -join ', '). Provision them on the runner host."
|
||||||
|
}
|
||||||
|
Write-Host ("pwsh: " + (pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'))
|
||||||
|
Write-Host ("dotnet: " + (dotnet --version))
|
||||||
|
Write-Host '--- dotnet --info ---'
|
||||||
|
dotnet --info
|
||||||
|
Write-Host '--- disk free ---'
|
||||||
|
df -h .
|
||||||
|
Write-Host '--- memory ---'
|
||||||
|
free -m
|
||||||
|
|
||||||
|
- name: Restore NuGet packages
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Write-Host '==> dotnet restore src/PSInfisicalAPI/PSInfisicalAPI.csproj'
|
||||||
|
dotnet restore src/PSInfisicalAPI/PSInfisicalAPI.csproj --verbosity normal
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Restore of PSInfisicalAPI.csproj failed with exit code $LASTEXITCODE" }
|
||||||
|
Write-Host '==> dotnet restore src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj'
|
||||||
|
dotnet restore src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj --verbosity normal
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Restore of PSInfisicalAPI.Tests.csproj failed with exit code $LASTEXITCODE" }
|
||||||
|
|
||||||
|
- name: Build module
|
||||||
|
shell: pwsh
|
||||||
|
run: ./build.ps1
|
||||||
|
|
||||||
|
- name: Validate module manifest
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
|
||||||
|
$manifest = Test-ModuleManifest -Path $manifestPath
|
||||||
|
Write-Host "Manifest OK: $($manifest.Name) $($manifest.Version)"
|
||||||
|
|
||||||
|
- name: Upload module artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: PSInfisicalAPI-module
|
||||||
|
path: Module/PSInfisicalAPI
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: build
|
||||||
|
if: ${{ success() && github.event.pull_request.merged == true }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.meta.outputs.version }}
|
||||||
|
tag: ${{ steps.meta.outputs.tag }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Verify host prerequisites (pwsh)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if (-not (Get-Command pwsh -ErrorAction SilentlyContinue)) {
|
||||||
|
throw "Host runner is missing required tool: pwsh. Provision it on the runner host."
|
||||||
|
}
|
||||||
|
Write-Host ("pwsh: " + (pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'))
|
||||||
|
|
||||||
|
- name: Download module artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: PSInfisicalAPI-module
|
||||||
|
path: Module/PSInfisicalAPI
|
||||||
|
|
||||||
|
- name: Resolve module version and tag
|
||||||
|
id: meta
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
|
||||||
|
$manifest = Test-ModuleManifest -Path $manifestPath
|
||||||
|
$version = $manifest.Version.ToString()
|
||||||
|
$tag = $version
|
||||||
|
Write-Host "Module version: $version"
|
||||||
|
Write-Host "Release tag: $tag"
|
||||||
|
"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
|
||||||
|
"tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
|
||||||
|
|
||||||
|
- name: Package module as release asset
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.meta.outputs.version }}
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$zipPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip"
|
||||||
|
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
|
||||||
|
Compress-Archive -Path 'Module/PSInfisicalAPI/*' -DestinationPath $zipPath -Force
|
||||||
|
Write-Host "Created: $zipPath ($([math]::Round((Get-Item $zipPath).Length / 1KB, 1)) KB)"
|
||||||
|
|
||||||
|
- name: Create GitHub release
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
API_URL: ${{ github.api_url }}
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
TAG: ${{ steps.meta.outputs.tag }}
|
||||||
|
VERSION: ${{ steps.meta.outputs.version }}
|
||||||
|
COMMIT_SHA: ${{ github.sha }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||||
|
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||||
|
SERVER_URL: ${{ github.server_url }}
|
||||||
|
RUN_ID: ${{ github.run_id }}
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
trap { Write-Host "==> RELEASE STEP FAILED: $($_ | Out-String)"; Write-Host ($_.ScriptStackTrace); exit 1 }
|
||||||
|
|
||||||
|
Write-Host "==> [1/8] Validating inputs"
|
||||||
|
Write-Host " TAG=$($env:TAG)"
|
||||||
|
Write-Host " VERSION=$($env:VERSION)"
|
||||||
|
Write-Host " REPO=$($env:REPO)"
|
||||||
|
Write-Host " API_URL=$($env:API_URL)"
|
||||||
|
Write-Host " SERVER_URL=$($env:SERVER_URL)"
|
||||||
|
Write-Host " PR_NUMBER=$($env:PR_NUMBER)"
|
||||||
|
Write-Host " RUN_ID=$($env:RUN_ID)"
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:GITHUB_TOKEN)) { throw "github.token is empty." }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:TAG)) { throw "TAG is empty." }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:VERSION)) { throw "VERSION is empty." }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:API_URL)) { throw "API_URL is empty." }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:REPO)) { throw "REPO is empty." }
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:COMMIT_SHA)) { throw "COMMIT_SHA is empty." }
|
||||||
|
|
||||||
|
Write-Host "==> [2/8] Deriving metadata"
|
||||||
|
$shortSha = $env:COMMIT_SHA.Substring(0, [Math]::Min(12, $env:COMMIT_SHA.Length))
|
||||||
|
$buildUtc = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
|
||||||
|
$runUrl = "$($env:SERVER_URL)/$($env:REPO)/actions/runs/$($env:RUN_ID)"
|
||||||
|
$prUrl = "$($env:SERVER_URL)/$($env:REPO)/pull/$($env:PR_NUMBER)"
|
||||||
|
Write-Host " shortSha=$shortSha"
|
||||||
|
|
||||||
|
Write-Host "==> [3/8] Extracting CHANGELOG section"
|
||||||
|
$changelogSection = ''
|
||||||
|
if (Test-Path 'CHANGELOG.md') {
|
||||||
|
$lines = [System.IO.File]::ReadAllLines('CHANGELOG.md')
|
||||||
|
$start = -1; $end = $lines.Length
|
||||||
|
for ($i = 0; $i -lt $lines.Length; $i++) {
|
||||||
|
if ($lines[$i] -match "^##\s+$([regex]::Escape($env:VERSION))\s*$") { $start = $i + 1; continue }
|
||||||
|
if ($start -ge 0 -and $lines[$i] -match '^##\s+') { $end = $i; break }
|
||||||
|
}
|
||||||
|
if ($start -ge 0) {
|
||||||
|
$changelogSection = ($lines[$start..($end - 1)] -join "`n").Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host " CHANGELOG section length: $($changelogSection.Length) chars"
|
||||||
|
|
||||||
|
Write-Host "==> [4/8] Building release body"
|
||||||
|
$changelogText = if ($changelogSection) { $changelogSection } else { '_No CHANGELOG section found for this version._' }
|
||||||
|
$sb = New-Object System.Text.StringBuilder
|
||||||
|
[void]$sb.AppendLine("**PSInfisicalAPI $($env:VERSION)**")
|
||||||
|
[void]$sb.AppendLine('')
|
||||||
|
[void]$sb.AppendLine('| Field | Value |')
|
||||||
|
[void]$sb.AppendLine('| --- | --- |')
|
||||||
|
[void]$sb.AppendLine("| Version | ``$($env:VERSION)`` |")
|
||||||
|
[void]$sb.AppendLine("| Tag | ``$($env:TAG)`` |")
|
||||||
|
[void]$sb.AppendLine("| Commit | [``$shortSha``]($($env:SERVER_URL)/$($env:REPO)/commit/$($env:COMMIT_SHA)) |")
|
||||||
|
[void]$sb.AppendLine("| Built (UTC) | $buildUtc |")
|
||||||
|
[void]$sb.AppendLine("| Merged PR | [#$($env:PR_NUMBER) $($env:PR_TITLE)]($prUrl) by @$($env:PR_AUTHOR) |")
|
||||||
|
[void]$sb.AppendLine("| Workflow run | [$($env:RUN_ID)]($runUrl) |")
|
||||||
|
[void]$sb.AppendLine('')
|
||||||
|
[void]$sb.AppendLine('## Changes')
|
||||||
|
[void]$sb.AppendLine($changelogText)
|
||||||
|
[void]$sb.AppendLine('')
|
||||||
|
[void]$sb.AppendLine('## Install')
|
||||||
|
[void]$sb.AppendLine('```powershell')
|
||||||
|
[void]$sb.AppendLine("Install-Module -Name PSInfisicalAPI -RequiredVersion $($env:VERSION) -Scope CurrentUser")
|
||||||
|
[void]$sb.AppendLine('```')
|
||||||
|
$body = $sb.ToString()
|
||||||
|
Write-Host " body length: $($body.Length) chars"
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
Authorization = "Bearer $($env:GITHUB_TOKEN)"
|
||||||
|
Accept = 'application/vnd.github+json'
|
||||||
|
'X-GitHub-Api-Version' = '2022-11-28'
|
||||||
|
}
|
||||||
|
$createUri = "$($env:API_URL)/repos/$($env:REPO)/releases"
|
||||||
|
|
||||||
|
Write-Host "==> [5/8] Checking for existing release tag: $createUri/tags/$($env:TAG)"
|
||||||
|
$existing = $null
|
||||||
|
try {
|
||||||
|
$existing = Invoke-RestMethod -Method Get -Headers $headers `
|
||||||
|
-Uri "$createUri/tags/$($env:TAG)" -ErrorAction Stop
|
||||||
|
} catch {
|
||||||
|
$status = $null
|
||||||
|
try { $status = $_.Exception.Response.StatusCode.value__ } catch { }
|
||||||
|
if ($status -ne 404) {
|
||||||
|
Write-Host " Lookup failed (status=$status): $($_.Exception.Message)"
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
Write-Host " No existing release (404)."
|
||||||
|
}
|
||||||
|
if ($existing) {
|
||||||
|
Write-Host " Release tag '$($env:TAG)' already exists (id=$($existing.id)); skipping creation."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "==> [6/8] Creating release"
|
||||||
|
$payload = @{
|
||||||
|
tag_name = $env:TAG
|
||||||
|
target_commitish = $env:COMMIT_SHA
|
||||||
|
name = "PSInfisicalAPI $($env:VERSION)"
|
||||||
|
body = $body
|
||||||
|
draft = $false
|
||||||
|
prerelease = $false
|
||||||
|
} | ConvertTo-Json -Depth 4
|
||||||
|
Write-Host " payload bytes: $([System.Text.Encoding]::UTF8.GetByteCount($payload))"
|
||||||
|
|
||||||
|
$release = Invoke-RestMethod -Method Post -Uri $createUri -Headers $headers `
|
||||||
|
-ContentType 'application/json' -Body $payload
|
||||||
|
Write-Host " Created release id=$($release.id) at $($release.html_url)"
|
||||||
|
|
||||||
|
Write-Host "==> [7/8] Locating release asset"
|
||||||
|
$assetPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip"
|
||||||
|
if (-not (Test-Path $assetPath)) { throw "Release asset not found at: $assetPath" }
|
||||||
|
$fileBytes = [System.IO.File]::ReadAllBytes($assetPath)
|
||||||
|
Write-Host " Asset: $assetPath ($([math]::Round($fileBytes.Length / 1KB, 1)) KB)"
|
||||||
|
|
||||||
|
Write-Host "==> [8/8] Uploading asset"
|
||||||
|
# GitHub returns a URI Template in upload_url (e.g. "https://uploads.github.com/.../assets{?name,label}").
|
||||||
|
# Strip the template suffix and append the asset name query.
|
||||||
|
$uploadBase = ($release.upload_url -replace '\{.*\}$', '')
|
||||||
|
$uploadUri = "$uploadBase`?name=PSInfisicalAPI-$($env:VERSION).zip"
|
||||||
|
Invoke-RestMethod -Method Post -Uri $uploadUri -Headers $headers `
|
||||||
|
-ContentType 'application/zip' -Body $fileBytes | Out-Null
|
||||||
|
Write-Host "==> Done: uploaded PSInfisicalAPI-$($env:VERSION).zip"
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: release
|
||||||
|
if: ${{ success() && github.event.pull_request.merged == true }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Verify host prerequisites (pwsh)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if (-not (Get-Command pwsh -ErrorAction SilentlyContinue)) {
|
||||||
|
throw "Host runner is missing required tool: pwsh. Provision it on the runner host."
|
||||||
|
}
|
||||||
|
Write-Host ("pwsh: " + (pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'))
|
||||||
|
|
||||||
|
- name: Download module artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: PSInfisicalAPI-module
|
||||||
|
path: Module/PSInfisicalAPI
|
||||||
|
|
||||||
|
- name: Bootstrap Microsoft.PowerShell.PSResourceGet
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if (-not (Get-Module -ListAvailable -Name Microsoft.PowerShell.PSResourceGet)) {
|
||||||
|
Write-Host "==> Installing Microsoft.PowerShell.PSResourceGet for CurrentUser"
|
||||||
|
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
|
||||||
|
}
|
||||||
|
Import-Module Microsoft.PowerShell.PSResourceGet -ErrorAction Stop
|
||||||
|
|
||||||
|
$existing = Get-PSResourceRepository -Name PSGallery -ErrorAction SilentlyContinue
|
||||||
|
if (-not $existing) {
|
||||||
|
Write-Host "==> Registering PSGallery repository"
|
||||||
|
Register-PSResourceRepository -PSGallery -Trusted -ErrorAction Stop
|
||||||
|
} else {
|
||||||
|
Write-Host "==> PSGallery already registered; ensuring Trusted + ApiVersion v2"
|
||||||
|
Set-PSResourceRepository -Name PSGallery -Trusted -ApiVersion v2 -ErrorAction Stop
|
||||||
|
}
|
||||||
|
Get-PSResourceRepository -Name PSGallery | Format-Table Name,Uri,Trusted,ApiVersion
|
||||||
|
|
||||||
|
- name: Verify PowerShell Gallery API key is configured
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}
|
||||||
|
run: |
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:PSGALLERY_API_KEY)) {
|
||||||
|
throw "Repository secret 'PSGALLERY_API_KEY' is not configured."
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Re-validate downloaded module manifest
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
|
||||||
|
$manifest = Test-ModuleManifest -Path $manifestPath
|
||||||
|
Write-Host "Manifest OK: $($manifest.Name) $($manifest.Version)"
|
||||||
|
|
||||||
|
- name: Publish to PowerShell Gallery
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$moduleDir = Join-Path $PWD 'Module/PSInfisicalAPI'
|
||||||
|
Write-Host "Publishing module from: $moduleDir"
|
||||||
|
Publish-PSResource `
|
||||||
|
-Path $moduleDir `
|
||||||
|
-Repository PSGallery `
|
||||||
|
-ApiKey $env:PSGALLERY_API_KEY `
|
||||||
|
-Verbose
|
||||||
+85
-8
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
@{
|
@{
|
||||||
RootModule = 'PSInfisicalAPI.psm1'
|
RootModule = 'PSInfisicalAPI.psm1'
|
||||||
ModuleVersion = '2026.06.06.2229'
|
ModuleVersion = '2026.06.16.0217'
|
||||||
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
|
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
|
||||||
Author = 'Grace Solutions'
|
Author = 'Grace Solutions'
|
||||||
CompanyName = 'Grace Solutions'
|
CompanyName = 'Grace Solutions'
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
'Copy-InfisicalSecret',
|
'Copy-InfisicalSecret',
|
||||||
'ConvertTo-InfisicalSecretDictionary',
|
'ConvertTo-InfisicalSecretDictionary',
|
||||||
'Export-InfisicalSecrets',
|
'Export-InfisicalSecrets',
|
||||||
|
'Import-InfisicalSecret',
|
||||||
'Get-InfisicalProject',
|
'Get-InfisicalProject',
|
||||||
'New-InfisicalProject',
|
'New-InfisicalProject',
|
||||||
'Update-InfisicalProject',
|
'Update-InfisicalProject',
|
||||||
@@ -35,6 +36,14 @@
|
|||||||
'New-InfisicalTag',
|
'New-InfisicalTag',
|
||||||
'Update-InfisicalTag',
|
'Update-InfisicalTag',
|
||||||
'Remove-InfisicalTag',
|
'Remove-InfisicalTag',
|
||||||
|
'Get-InfisicalOrganization',
|
||||||
|
'New-InfisicalOrganization',
|
||||||
|
'Update-InfisicalOrganization',
|
||||||
|
'Remove-InfisicalOrganization',
|
||||||
|
'Get-InfisicalSubOrganization',
|
||||||
|
'New-InfisicalSubOrganization',
|
||||||
|
'Update-InfisicalSubOrganization',
|
||||||
|
'Remove-InfisicalSubOrganization',
|
||||||
'Get-InfisicalCertificateAuthority',
|
'Get-InfisicalCertificateAuthority',
|
||||||
'Get-InfisicalPkiSubscriber',
|
'Get-InfisicalPkiSubscriber',
|
||||||
'Get-InfisicalCertificateProfile',
|
'Get-InfisicalCertificateProfile',
|
||||||
@@ -51,7 +60,9 @@
|
|||||||
'Get-InfisicalScepMdmProfile',
|
'Get-InfisicalScepMdmProfile',
|
||||||
'Export-InfisicalScepMdmProfile',
|
'Export-InfisicalScepMdmProfile',
|
||||||
'Write-InfisicalScepMdmProfileToWmi',
|
'Write-InfisicalScepMdmProfileToWmi',
|
||||||
'Start-InfisicalProcess'
|
'Start-InfisicalProcess',
|
||||||
|
'Get-InfisicalEnvironmentVariable',
|
||||||
|
'Get-InfisicalSANList'
|
||||||
)
|
)
|
||||||
AliasesToExport = @()
|
AliasesToExport = @()
|
||||||
VariablesToExport = @()
|
VariablesToExport = @()
|
||||||
@@ -63,7 +74,7 @@
|
|||||||
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
|
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
|
||||||
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
|
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
|
||||||
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
|
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
|
||||||
CommitHash = '207e7429e448'
|
CommitHash = '6318d06362ad'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -295,7 +295,7 @@ $CopyInfisicalSecretResult = Copy-InfisicalSecret @CopyInfisicalSecretParameters
|
|||||||
<command:noun>InfisicalSecretDictionary</command:noun>
|
<command:noun>InfisicalSecretDictionary</command:noun>
|
||||||
</command:details>
|
</command:details>
|
||||||
<maml:description>
|
<maml:description>
|
||||||
<maml:para>Aggregates an incoming pipeline of InfisicalSecret objects into a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText to materialize string values. Duplicate keys are handled via the -DuplicateKeyBehavior parameter (Error, FirstWins, LastWins).</maml:para>
|
<maml:para>Aggregates an incoming pipeline of InfisicalSecret objects into a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText to materialize string values. Duplicate keys are handled via the -DuplicateKeyBehavior parameter (Error, FirstWins, LastWins). -Prefix prepends a string to every dictionary key (e.g. SecretName 'API_KEY' with -Prefix 'MYAPP_' becomes key 'MYAPP_API_KEY'); the underlying InfisicalSecret objects are not mutated. A SecretName that already starts with -Prefix (case-insensitive) is left as-is to avoid double-prefixing; pass -ForcePrefix to always prepend.</maml:para>
|
||||||
</maml:description>
|
</maml:description>
|
||||||
<maml:alertSet>
|
<maml:alertSet>
|
||||||
<maml:title>Notes</maml:title>
|
<maml:title>Notes</maml:title>
|
||||||
@@ -322,6 +322,11 @@ $ConvertToInfisicalSecretDictionaryParameters.Verbose = $True
|
|||||||
$ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary @ConvertToInfisicalSecretDictionaryParameters</dev:code>
|
$ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary @ConvertToInfisicalSecretDictionaryParameters</dev:code>
|
||||||
<dev:remarks><maml:para>Aggregates recursive secret results into a plain-text dictionary, with the last value winning on key collisions.</maml:para></dev:remarks>
|
<dev:remarks><maml:para>Aggregates recursive secret results into a plain-text dictionary, with the last value winning on key collisions.</maml:para></dev:remarks>
|
||||||
</command:example>
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 3</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSecret -ProjectId $ProjectId -Environment 'dev' | ConvertTo-InfisicalSecretDictionary -Prefix 'MYAPP_' -AsPlainText</dev:code>
|
||||||
|
<dev:remarks><maml:para>Builds a plain-text dictionary whose keys are namespaced with 'MYAPP_' (e.g. API_KEY becomes MYAPP_API_KEY); the source InfisicalSecret objects are unchanged.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
</command:examples>
|
</command:examples>
|
||||||
</command:command>
|
</command:command>
|
||||||
|
|
||||||
@@ -333,7 +338,7 @@ $ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary
|
|||||||
<command:noun>InfisicalSecrets</command:noun>
|
<command:noun>InfisicalSecrets</command:noun>
|
||||||
</command:details>
|
</command:details>
|
||||||
<maml:description>
|
<maml:description>
|
||||||
<maml:para>Buffers an incoming pipeline of InfisicalSecret objects and writes them to a file in the requested format (DotEnv, Json, Yaml, EnvironmentVariables, etc.) or sets them as environment variables on the chosen scope (Process, User, Machine). -Encoding controls text encoding for file outputs. -Prefix prepends a string to every emitted variable name regardless of format.</maml:para>
|
<maml:para>Buffers an incoming pipeline of InfisicalSecret objects and writes them to a file in the requested format (DotEnv, Json, Yaml, EnvironmentVariables, etc.) or sets them as environment variables on the chosen scope (Process, User, Machine). -Encoding controls text encoding for file outputs. -Prefix prepends a string to every emitted variable name regardless of format; names that already start with -Prefix (case-insensitive) are left as-is to avoid double-prefixing. Pass -ForcePrefix to always prepend.</maml:para>
|
||||||
</maml:description>
|
</maml:description>
|
||||||
<maml:alertSet>
|
<maml:alertSet>
|
||||||
<maml:title>Notes</maml:title>
|
<maml:title>Notes</maml:title>
|
||||||
@@ -369,6 +374,44 @@ $ExportInfisicalSecretsResult = Export-InfisicalSecrets @ExportInfisicalSecretsP
|
|||||||
</command:examples>
|
</command:examples>
|
||||||
</command:command>
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Import-InfisicalSecret</command:name>
|
||||||
|
<maml:description><maml:para>Reads a previously exported secrets file (Json, Yaml, Env, or Xml) back into a name-keyed Dictionary.</maml:para></maml:description>
|
||||||
|
<command:verb>Import</command:verb>
|
||||||
|
<command:noun>InfisicalSecret</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Loads the file at -Path (which must exist) using the parser matching -Format and returns a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText for a plain string dictionary. -Prefix prepends to every emitted key; keys already starting with -Prefix (case-insensitive) are left as-is to avoid double-prefixing, and -ForcePrefix overrides that gate. -DuplicateKeyBehavior controls collision handling (Error, FirstWins, LastWins). The EnvironmentVariables format is intentionally not supported here; use [Environment]::GetEnvironmentVariable or Get-InfisicalEnvironmentVariable for environment-backed values.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Importers expect the same schema that Export-InfisicalSecrets produces (Json/Yaml = array or 'Secrets' root list of {SecretName, SecretValue}; Xml = <Secrets><Secret><SecretName/><SecretValue/>; Env = KEY=VALUE per line, '#' comments allowed). JSON and YAML additionally accept a flat key/value object as a convenience.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>$Secrets = Import-InfisicalSecret -Path '.\secrets.json' -Format Json</dev:code>
|
||||||
|
<dev:remarks><maml:para>Reads a JSON export back into a Dictionary<string, SecureString> keyed by the original SecretName values.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$ImportInfisicalSecretParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$ImportInfisicalSecretParameters.Path = [System.IO.FileInfo]'.\secrets.env'
|
||||||
|
$ImportInfisicalSecretParameters.Format = 'Env'
|
||||||
|
$ImportInfisicalSecretParameters.Prefix = 'MYAPP_'
|
||||||
|
$ImportInfisicalSecretParameters.DuplicateKeyBehavior = 'LastWins'
|
||||||
|
$ImportInfisicalSecretParameters.AsPlainText = $True
|
||||||
|
|
||||||
|
$ImportInfisicalSecretResult = Import-InfisicalSecret @ImportInfisicalSecretParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Loads a .env file into a plain-text dictionary, namespacing every key with 'MYAPP_' and letting the last occurrence win on duplicates.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
|
||||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
<command:details>
|
<command:details>
|
||||||
<command:name>Get-InfisicalProject</command:name>
|
<command:name>Get-InfisicalProject</command:name>
|
||||||
@@ -1662,7 +1705,7 @@ $WriteInfisicalScepMdmProfileToWmiResult = Write-InfisicalScepMdmProfileToWmi @W
|
|||||||
<command:noun>InfisicalProcess</command:noun>
|
<command:noun>InfisicalProcess</command:noun>
|
||||||
</command:details>
|
</command:details>
|
||||||
<maml:description>
|
<maml:description>
|
||||||
<maml:para>Launches the executable specified by -FilePath, captures stdout/stderr, validates the exit code against -AcceptableExitCodeList, and optionally parses output with -ParsingExpression. InfisicalSecret objects supplied via -Secret (pipeline or by name) are decrypted into the ProcessStartInfo.Environment dictionary only, never written to the user or machine scope; -Prefix prepends a string to each injected variable name. -EnvironmentVariables adds additional non-secret values. -ExecutionTimeout, -NoWait, -CreateNoWindow, -WindowStyle, -Priority, -StandardInputObjectList, -SecureArgumentList, -LogOutput, and -ContinueOnError mirror the semantics of the upstream Start-ProcessWithOutput helper. Honors -WhatIf and -Confirm.</maml:para>
|
<maml:para>Launches the executable specified by -FilePath, captures stdout/stderr, validates the exit code against -AcceptableExitCodeList, and optionally parses output with -ParsingExpression. InfisicalSecret objects supplied via -Secret (pipeline or by name) are decrypted into the ProcessStartInfo.Environment dictionary only, never written to the user or machine scope; -Prefix prepends a string to each injected variable name, skipping names that already start with -Prefix (case-insensitive) unless -ForcePrefix is supplied. -EnvironmentVariables adds additional non-secret values. -ExecutionTimeout, -NoWait, -CreateNoWindow, -WindowStyle, -Priority, -StandardInputObjectList, -SecureArgumentList, -LogOutput, and -ContinueOnError mirror the semantics of the upstream Start-ProcessWithOutput helper. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
</maml:description>
|
</maml:description>
|
||||||
<maml:alertSet>
|
<maml:alertSet>
|
||||||
<maml:title>Notes</maml:title>
|
<maml:title>Notes</maml:title>
|
||||||
@@ -1701,4 +1744,353 @@ $StartInfisicalProcessResult = Start-InfisicalProcess @StartInfisicalProcessPara
|
|||||||
</command:examples>
|
</command:examples>
|
||||||
</command:command>
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalEnvironmentVariable</command:name>
|
||||||
|
<maml:description><maml:para>Reads an environment variable from the first scope that has a non-empty value (Process > User > Machine).</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalEnvironmentVariable</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Returns the value of -Name from the first scope that contains a non-empty value, checking Process, then User, then Machine in that order. Emits nothing when the variable is missing or blank in every scope, so an assignment yields $null without writing errors or warnings. Platform-unsupported scopes (User and Machine on non-Windows) are silently skipped.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Designed for the same discovery semantics the rest of PSInfisicalAPI uses when resolving connection inputs from the environment. Pipe-friendly: accepts -Name from the pipeline by value and by property name.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>$Value = Get-InfisicalEnvironmentVariable -Name 'INFISICAL_CLIENT_ID'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Returns the first non-empty value of INFISICAL_CLIENT_ID across Process, User, and Machine scopes; assigns $null when the variable is unset everywhere.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>@('INFISICAL_BASE_URI','INFISICAL_PROJECT_ID') | Get-InfisicalEnvironmentVariable</dev:code>
|
||||||
|
<dev:remarks><maml:para>Pipes a list of variable names through the cmdlet and emits one value per name that is set in any scope.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Lists or retrieves Infisical organizations accessible to the current identity.</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Default (List parameter set) returns every organization the active session can see; visibility is governed by Infisical's role assignments. When -OrganizationId is supplied (Single parameter set) the cmdlet returns one organization. Does not require a project context.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>The List-mode result is an array of InfisicalOrganization objects; pipe into Where-Object or Select-Object to filter by Slug, Name, or Id. The cmdlet accepts pipeline input by property name on -OrganizationId.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Get-InfisicalOrganization</dev:code>
|
||||||
|
<dev:remarks><maml:para>Lists every organization the current session can see.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>Get-InfisicalOrganization -OrganizationId $OrganizationId</dev:code>
|
||||||
|
<dev:remarks><maml:para>Retrieves the canonical record for a single organization by id.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>New-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Creates a new Infisical organization.</maml:para></maml:description>
|
||||||
|
<command:verb>New</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Creates a new organization with the supplied name and optional slug. Honors -WhatIf and -Confirm. Requires server-side permission to create organizations.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Slug must be unique server-side; if omitted, the server derives one from the name.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>New-InfisicalOrganization -Name 'Acme Corporation'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Creates a new organization named 'Acme Corporation' with a server-derived slug.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$NewInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$NewInfisicalOrganizationParameters.Name = 'Acme Corporation'
|
||||||
|
$NewInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||||
|
$NewInfisicalOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$NewInfisicalOrganizationResult = New-InfisicalOrganization @NewInfisicalOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Creates an organization with an explicit slug.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Update-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Updates mutable attributes on an existing Infisical organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Update</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Updates the name or slug of an organization. -OrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Renaming or re-slugging an organization may affect billing exports and identity URLs; coordinate with downstream consumers.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Update-InfisicalOrganization -OrganizationId $OrganizationId -Name 'Acme Corp.'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the supplied organization.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$UpdateInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$UpdateInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||||
|
$UpdateInfisicalOrganizationParameters.Name = 'Acme Corp.'
|
||||||
|
$UpdateInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||||
|
$UpdateInfisicalOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$UpdateInfisicalOrganizationResult = Update-InfisicalOrganization @UpdateInfisicalOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the organization and updates its slug.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Remove-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Deletes an Infisical organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Remove</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Deletes an organization by id. -OrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed organization id.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>This is irreversible and removes all projects, sub-organizations, secrets, and identities owned by the organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Remove-InfisicalOrganization -OrganizationId $OrganizationId -Confirm:$False</dev:code>
|
||||||
|
<dev:remarks><maml:para>Deletes the supplied organization without prompting.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$RemoveInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$RemoveInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||||
|
$RemoveInfisicalOrganizationParameters.PassThru = $True
|
||||||
|
$RemoveInfisicalOrganizationParameters.Confirm = $False
|
||||||
|
$RemoveInfisicalOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$RemoveInfisicalOrganizationResult = Remove-InfisicalOrganization @RemoveInfisicalOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Removes the organization without confirmation and emits the removed organization id for logging.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Lists or retrieves Infisical sub-organizations accessible to the current identity.</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Default (List parameter set) returns every sub-organization the active session can see. Optional -Limit, -Offset, -Search, -OrderBy, -OrderDirection, and -IsAccessible are forwarded to the server as query parameters. When -SubOrganizationId is supplied (Single parameter set) the cmdlet returns one sub-organization. Does not require a project context.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Sub-organizations are a beta Infisical feature. The List result is an array of InfisicalSubOrganization objects; pipe into Where-Object or Select-Object to filter further. The cmdlet accepts pipeline input by property name on -SubOrganizationId.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSubOrganization</dev:code>
|
||||||
|
<dev:remarks><maml:para>Lists every sub-organization the current session can see.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId</dev:code>
|
||||||
|
<dev:remarks><maml:para>Retrieves the canonical record for a single sub-organization by id.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 3</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSubOrganization -Search 'platform' -OrderBy 'name' -OrderDirection 'asc' -Limit 25 -IsAccessible</dev:code>
|
||||||
|
<dev:remarks><maml:para>Lists up to 25 sub-organizations matching 'platform', sorted ascending by name, restricted to those the current identity has access to.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>New-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Creates a new Infisical sub-organization.</maml:para></maml:description>
|
||||||
|
<command:verb>New</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Creates a sub-organization with the supplied name and slug. Both are required by the server. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Slug must be unique within the parent organization. Sub-organizations are a beta Infisical feature.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>New-InfisicalSubOrganization -Name 'Platform Engineering' -Slug 'platform-eng'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Creates a new sub-organization named 'Platform Engineering' with slug 'platform-eng'.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$NewInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$NewInfisicalSubOrganizationParameters.Name = 'Platform Engineering'
|
||||||
|
$NewInfisicalSubOrganizationParameters.Slug = 'platform-eng'
|
||||||
|
$NewInfisicalSubOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$NewInfisicalSubOrganizationResult = New-InfisicalSubOrganization @NewInfisicalSubOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Splatted invocation that creates a sub-organization and logs the request via the verbose stream.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Update-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Updates mutable attributes on an existing Infisical sub-organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Update</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Updates the name or slug of a sub-organization. -SubOrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Sub-organizations are a beta Infisical feature; coordinate slug changes with downstream consumers that pin the slug in scripts or configuration files.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Update-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Name 'Platform (v2)'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the supplied sub-organization.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$UpdateInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.Name = 'Platform (v2)'
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.Slug = 'platform-v2'
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$UpdateInfisicalSubOrganizationResult = Update-InfisicalSubOrganization @UpdateInfisicalSubOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the sub-organization and updates its slug in a single call.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Remove-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Deletes an Infisical sub-organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Remove</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Deletes a sub-organization by id. -SubOrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed sub-organization id.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>This is destructive and removes all projects, secrets, and identities scoped to the sub-organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Remove-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Confirm:$False</dev:code>
|
||||||
|
<dev:remarks><maml:para>Deletes the supplied sub-organization without prompting.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$RemoveInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.PassThru = $True
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.Confirm = $False
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$RemoveInfisicalSubOrganizationResult = Remove-InfisicalSubOrganization @RemoveInfisicalSubOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Removes the sub-organization without confirmation and emits the removed id for logging.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalSANList</command:name>
|
||||||
|
<maml:description><maml:para>Builds a deduplicated list of Subject Alternative Name candidates for the local device.</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalSANList</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Returns, in order: the local device name; the device name suffixed with each non-empty DNS suffix found on any operational (non-loopback) network adapter and the system primary domain; every IPv4 unicast address whose first octets fall within RFC 1918 (10/8, 172.16/12, 192.168/16) or CGNAT (100.64/10); and the IPv4 and IPv6 loopback addresses (127.0.0.1, ::1). Optional -InclusionExpression and -ExclusionExpression regex filters are applied in that order after collection, before output. Suitable as a one-shot SAN provider for Request-InfisicalCertificate -DnsName.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Output is a single strongly-typed System.String[] array (emitted non-enumerated) so it round-trips into [System.Collections.Generic.List[string]]::AddRange() and binds directly to string[] parameters such as Request-InfisicalCertificate -DnsName. The device name comes first so it can be reused as a CommonName. Routable public IPv4 addresses, link-local addresses, and IPv6 unicast addresses other than loopback are intentionally excluded. -InclusionExpression and -ExclusionExpression are case-insensitive .NET regular expressions; inclusion is applied first, then exclusion.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSANList</dev:code>
|
||||||
|
<dev:remarks><maml:para>Returns the SAN candidate list for the current device.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$Sans = Get-InfisicalSANList
|
||||||
|
Request-InfisicalCertificate -ProjectId $ProjectId -CertificateAuthorityId $CaId -CommonName $Sans[0] -DnsName $Sans -Ttl '90d'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Captures the SAN list, then uses the device name as the CommonName and the full list as DnsName when requesting a certificate.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 3</maml:title>
|
||||||
|
<dev:code>$GetInfisicalSANListParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$GetInfisicalSANListParameters.InclusionExpression = '\.gracesolution\.prv$|^10\.|^172\.'
|
||||||
|
$GetInfisicalSANListParameters.ExclusionExpression = '^127\.|^::1$'
|
||||||
|
$Sans = Get-InfisicalSANList @GetInfisicalSANListParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Keeps only entries ending in the corporate DNS suffix or sitting in the 10/8 or 172/12 ranges, then drops loopback. Filters are case-insensitive and applied in fetch -> include -> exclude -> output order.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
</helpItems>
|
</helpItems>
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ $CopyInfisicalSecretResult = Copy-InfisicalSecret @CopyInfisicalSecretParameters
|
|||||||
<command:noun>InfisicalSecretDictionary</command:noun>
|
<command:noun>InfisicalSecretDictionary</command:noun>
|
||||||
</command:details>
|
</command:details>
|
||||||
<maml:description>
|
<maml:description>
|
||||||
<maml:para>Aggregates an incoming pipeline of InfisicalSecret objects into a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText to materialize string values. Duplicate keys are handled via the -DuplicateKeyBehavior parameter (Error, FirstWins, LastWins).</maml:para>
|
<maml:para>Aggregates an incoming pipeline of InfisicalSecret objects into a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText to materialize string values. Duplicate keys are handled via the -DuplicateKeyBehavior parameter (Error, FirstWins, LastWins). -Prefix prepends a string to every dictionary key (e.g. SecretName 'API_KEY' with -Prefix 'MYAPP_' becomes key 'MYAPP_API_KEY'); the underlying InfisicalSecret objects are not mutated. A SecretName that already starts with -Prefix (case-insensitive) is left as-is to avoid double-prefixing; pass -ForcePrefix to always prepend.</maml:para>
|
||||||
</maml:description>
|
</maml:description>
|
||||||
<maml:alertSet>
|
<maml:alertSet>
|
||||||
<maml:title>Notes</maml:title>
|
<maml:title>Notes</maml:title>
|
||||||
@@ -322,6 +322,11 @@ $ConvertToInfisicalSecretDictionaryParameters.Verbose = $True
|
|||||||
$ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary @ConvertToInfisicalSecretDictionaryParameters</dev:code>
|
$ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary @ConvertToInfisicalSecretDictionaryParameters</dev:code>
|
||||||
<dev:remarks><maml:para>Aggregates recursive secret results into a plain-text dictionary, with the last value winning on key collisions.</maml:para></dev:remarks>
|
<dev:remarks><maml:para>Aggregates recursive secret results into a plain-text dictionary, with the last value winning on key collisions.</maml:para></dev:remarks>
|
||||||
</command:example>
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 3</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSecret -ProjectId $ProjectId -Environment 'dev' | ConvertTo-InfisicalSecretDictionary -Prefix 'MYAPP_' -AsPlainText</dev:code>
|
||||||
|
<dev:remarks><maml:para>Builds a plain-text dictionary whose keys are namespaced with 'MYAPP_' (e.g. API_KEY becomes MYAPP_API_KEY); the source InfisicalSecret objects are unchanged.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
</command:examples>
|
</command:examples>
|
||||||
</command:command>
|
</command:command>
|
||||||
|
|
||||||
@@ -333,7 +338,7 @@ $ConvertToInfisicalSecretDictionaryResult = ConvertTo-InfisicalSecretDictionary
|
|||||||
<command:noun>InfisicalSecrets</command:noun>
|
<command:noun>InfisicalSecrets</command:noun>
|
||||||
</command:details>
|
</command:details>
|
||||||
<maml:description>
|
<maml:description>
|
||||||
<maml:para>Buffers an incoming pipeline of InfisicalSecret objects and writes them to a file in the requested format (DotEnv, Json, Yaml, EnvironmentVariables, etc.) or sets them as environment variables on the chosen scope (Process, User, Machine). -Encoding controls text encoding for file outputs. -Prefix prepends a string to every emitted variable name regardless of format.</maml:para>
|
<maml:para>Buffers an incoming pipeline of InfisicalSecret objects and writes them to a file in the requested format (DotEnv, Json, Yaml, EnvironmentVariables, etc.) or sets them as environment variables on the chosen scope (Process, User, Machine). -Encoding controls text encoding for file outputs. -Prefix prepends a string to every emitted variable name regardless of format; names that already start with -Prefix (case-insensitive) are left as-is to avoid double-prefixing. Pass -ForcePrefix to always prepend.</maml:para>
|
||||||
</maml:description>
|
</maml:description>
|
||||||
<maml:alertSet>
|
<maml:alertSet>
|
||||||
<maml:title>Notes</maml:title>
|
<maml:title>Notes</maml:title>
|
||||||
@@ -369,6 +374,44 @@ $ExportInfisicalSecretsResult = Export-InfisicalSecrets @ExportInfisicalSecretsP
|
|||||||
</command:examples>
|
</command:examples>
|
||||||
</command:command>
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Import-InfisicalSecret</command:name>
|
||||||
|
<maml:description><maml:para>Reads a previously exported secrets file (Json, Yaml, Env, or Xml) back into a name-keyed Dictionary.</maml:para></maml:description>
|
||||||
|
<command:verb>Import</command:verb>
|
||||||
|
<command:noun>InfisicalSecret</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Loads the file at -Path (which must exist) using the parser matching -Format and returns a case-insensitive Dictionary keyed by SecretName. By default values are SecureString; pass -AsPlainText for a plain string dictionary. -Prefix prepends to every emitted key; keys already starting with -Prefix (case-insensitive) are left as-is to avoid double-prefixing, and -ForcePrefix overrides that gate. -DuplicateKeyBehavior controls collision handling (Error, FirstWins, LastWins). The EnvironmentVariables format is intentionally not supported here; use [Environment]::GetEnvironmentVariable or Get-InfisicalEnvironmentVariable for environment-backed values.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Importers expect the same schema that Export-InfisicalSecrets produces (Json/Yaml = array or 'Secrets' root list of {SecretName, SecretValue}; Xml = <Secrets><Secret><SecretName/><SecretValue/>; Env = KEY=VALUE per line, '#' comments allowed). JSON and YAML additionally accept a flat key/value object as a convenience.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>$Secrets = Import-InfisicalSecret -Path '.\secrets.json' -Format Json</dev:code>
|
||||||
|
<dev:remarks><maml:para>Reads a JSON export back into a Dictionary<string, SecureString> keyed by the original SecretName values.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$ImportInfisicalSecretParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$ImportInfisicalSecretParameters.Path = [System.IO.FileInfo]'.\secrets.env'
|
||||||
|
$ImportInfisicalSecretParameters.Format = 'Env'
|
||||||
|
$ImportInfisicalSecretParameters.Prefix = 'MYAPP_'
|
||||||
|
$ImportInfisicalSecretParameters.DuplicateKeyBehavior = 'LastWins'
|
||||||
|
$ImportInfisicalSecretParameters.AsPlainText = $True
|
||||||
|
|
||||||
|
$ImportInfisicalSecretResult = Import-InfisicalSecret @ImportInfisicalSecretParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Loads a .env file into a plain-text dictionary, namespacing every key with 'MYAPP_' and letting the last occurrence win on duplicates.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
|
||||||
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
<command:details>
|
<command:details>
|
||||||
<command:name>Get-InfisicalProject</command:name>
|
<command:name>Get-InfisicalProject</command:name>
|
||||||
@@ -1662,7 +1705,7 @@ $WriteInfisicalScepMdmProfileToWmiResult = Write-InfisicalScepMdmProfileToWmi @W
|
|||||||
<command:noun>InfisicalProcess</command:noun>
|
<command:noun>InfisicalProcess</command:noun>
|
||||||
</command:details>
|
</command:details>
|
||||||
<maml:description>
|
<maml:description>
|
||||||
<maml:para>Launches the executable specified by -FilePath, captures stdout/stderr, validates the exit code against -AcceptableExitCodeList, and optionally parses output with -ParsingExpression. InfisicalSecret objects supplied via -Secret (pipeline or by name) are decrypted into the ProcessStartInfo.Environment dictionary only, never written to the user or machine scope; -Prefix prepends a string to each injected variable name. -EnvironmentVariables adds additional non-secret values. -ExecutionTimeout, -NoWait, -CreateNoWindow, -WindowStyle, -Priority, -StandardInputObjectList, -SecureArgumentList, -LogOutput, and -ContinueOnError mirror the semantics of the upstream Start-ProcessWithOutput helper. Honors -WhatIf and -Confirm.</maml:para>
|
<maml:para>Launches the executable specified by -FilePath, captures stdout/stderr, validates the exit code against -AcceptableExitCodeList, and optionally parses output with -ParsingExpression. InfisicalSecret objects supplied via -Secret (pipeline or by name) are decrypted into the ProcessStartInfo.Environment dictionary only, never written to the user or machine scope; -Prefix prepends a string to each injected variable name, skipping names that already start with -Prefix (case-insensitive) unless -ForcePrefix is supplied. -EnvironmentVariables adds additional non-secret values. -ExecutionTimeout, -NoWait, -CreateNoWindow, -WindowStyle, -Priority, -StandardInputObjectList, -SecureArgumentList, -LogOutput, and -ContinueOnError mirror the semantics of the upstream Start-ProcessWithOutput helper. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
</maml:description>
|
</maml:description>
|
||||||
<maml:alertSet>
|
<maml:alertSet>
|
||||||
<maml:title>Notes</maml:title>
|
<maml:title>Notes</maml:title>
|
||||||
@@ -1701,4 +1744,353 @@ $StartInfisicalProcessResult = Start-InfisicalProcess @StartInfisicalProcessPara
|
|||||||
</command:examples>
|
</command:examples>
|
||||||
</command:command>
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalEnvironmentVariable</command:name>
|
||||||
|
<maml:description><maml:para>Reads an environment variable from the first scope that has a non-empty value (Process > User > Machine).</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalEnvironmentVariable</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Returns the value of -Name from the first scope that contains a non-empty value, checking Process, then User, then Machine in that order. Emits nothing when the variable is missing or blank in every scope, so an assignment yields $null without writing errors or warnings. Platform-unsupported scopes (User and Machine on non-Windows) are silently skipped.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Designed for the same discovery semantics the rest of PSInfisicalAPI uses when resolving connection inputs from the environment. Pipe-friendly: accepts -Name from the pipeline by value and by property name.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>$Value = Get-InfisicalEnvironmentVariable -Name 'INFISICAL_CLIENT_ID'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Returns the first non-empty value of INFISICAL_CLIENT_ID across Process, User, and Machine scopes; assigns $null when the variable is unset everywhere.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>@('INFISICAL_BASE_URI','INFISICAL_PROJECT_ID') | Get-InfisicalEnvironmentVariable</dev:code>
|
||||||
|
<dev:remarks><maml:para>Pipes a list of variable names through the cmdlet and emits one value per name that is set in any scope.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Lists or retrieves Infisical organizations accessible to the current identity.</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Default (List parameter set) returns every organization the active session can see; visibility is governed by Infisical's role assignments. When -OrganizationId is supplied (Single parameter set) the cmdlet returns one organization. Does not require a project context.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>The List-mode result is an array of InfisicalOrganization objects; pipe into Where-Object or Select-Object to filter by Slug, Name, or Id. The cmdlet accepts pipeline input by property name on -OrganizationId.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Get-InfisicalOrganization</dev:code>
|
||||||
|
<dev:remarks><maml:para>Lists every organization the current session can see.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>Get-InfisicalOrganization -OrganizationId $OrganizationId</dev:code>
|
||||||
|
<dev:remarks><maml:para>Retrieves the canonical record for a single organization by id.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>New-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Creates a new Infisical organization.</maml:para></maml:description>
|
||||||
|
<command:verb>New</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Creates a new organization with the supplied name and optional slug. Honors -WhatIf and -Confirm. Requires server-side permission to create organizations.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Slug must be unique server-side; if omitted, the server derives one from the name.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>New-InfisicalOrganization -Name 'Acme Corporation'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Creates a new organization named 'Acme Corporation' with a server-derived slug.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$NewInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$NewInfisicalOrganizationParameters.Name = 'Acme Corporation'
|
||||||
|
$NewInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||||
|
$NewInfisicalOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$NewInfisicalOrganizationResult = New-InfisicalOrganization @NewInfisicalOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Creates an organization with an explicit slug.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Update-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Updates mutable attributes on an existing Infisical organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Update</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Updates the name or slug of an organization. -OrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Renaming or re-slugging an organization may affect billing exports and identity URLs; coordinate with downstream consumers.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Update-InfisicalOrganization -OrganizationId $OrganizationId -Name 'Acme Corp.'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the supplied organization.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$UpdateInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$UpdateInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||||
|
$UpdateInfisicalOrganizationParameters.Name = 'Acme Corp.'
|
||||||
|
$UpdateInfisicalOrganizationParameters.Slug = 'acme-corp'
|
||||||
|
$UpdateInfisicalOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$UpdateInfisicalOrganizationResult = Update-InfisicalOrganization @UpdateInfisicalOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the organization and updates its slug.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Remove-InfisicalOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Deletes an Infisical organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Remove</command:verb>
|
||||||
|
<command:noun>InfisicalOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Deletes an organization by id. -OrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed organization id.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>This is irreversible and removes all projects, sub-organizations, secrets, and identities owned by the organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Remove-InfisicalOrganization -OrganizationId $OrganizationId -Confirm:$False</dev:code>
|
||||||
|
<dev:remarks><maml:para>Deletes the supplied organization without prompting.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$RemoveInfisicalOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$RemoveInfisicalOrganizationParameters.OrganizationId = $OrganizationId
|
||||||
|
$RemoveInfisicalOrganizationParameters.PassThru = $True
|
||||||
|
$RemoveInfisicalOrganizationParameters.Confirm = $False
|
||||||
|
$RemoveInfisicalOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$RemoveInfisicalOrganizationResult = Remove-InfisicalOrganization @RemoveInfisicalOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Removes the organization without confirmation and emits the removed organization id for logging.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Lists or retrieves Infisical sub-organizations accessible to the current identity.</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Default (List parameter set) returns every sub-organization the active session can see. Optional -Limit, -Offset, -Search, -OrderBy, -OrderDirection, and -IsAccessible are forwarded to the server as query parameters. When -SubOrganizationId is supplied (Single parameter set) the cmdlet returns one sub-organization. Does not require a project context.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Sub-organizations are a beta Infisical feature. The List result is an array of InfisicalSubOrganization objects; pipe into Where-Object or Select-Object to filter further. The cmdlet accepts pipeline input by property name on -SubOrganizationId.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSubOrganization</dev:code>
|
||||||
|
<dev:remarks><maml:para>Lists every sub-organization the current session can see.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId</dev:code>
|
||||||
|
<dev:remarks><maml:para>Retrieves the canonical record for a single sub-organization by id.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 3</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSubOrganization -Search 'platform' -OrderBy 'name' -OrderDirection 'asc' -Limit 25 -IsAccessible</dev:code>
|
||||||
|
<dev:remarks><maml:para>Lists up to 25 sub-organizations matching 'platform', sorted ascending by name, restricted to those the current identity has access to.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>New-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Creates a new Infisical sub-organization.</maml:para></maml:description>
|
||||||
|
<command:verb>New</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Creates a sub-organization with the supplied name and slug. Both are required by the server. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Slug must be unique within the parent organization. Sub-organizations are a beta Infisical feature.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>New-InfisicalSubOrganization -Name 'Platform Engineering' -Slug 'platform-eng'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Creates a new sub-organization named 'Platform Engineering' with slug 'platform-eng'.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$NewInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$NewInfisicalSubOrganizationParameters.Name = 'Platform Engineering'
|
||||||
|
$NewInfisicalSubOrganizationParameters.Slug = 'platform-eng'
|
||||||
|
$NewInfisicalSubOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$NewInfisicalSubOrganizationResult = New-InfisicalSubOrganization @NewInfisicalSubOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Splatted invocation that creates a sub-organization and logs the request via the verbose stream.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Update-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Updates mutable attributes on an existing Infisical sub-organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Update</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Updates the name or slug of a sub-organization. -SubOrganizationId is required. Only bound parameters are transmitted. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Sub-organizations are a beta Infisical feature; coordinate slug changes with downstream consumers that pin the slug in scripts or configuration files.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Update-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Name 'Platform (v2)'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the supplied sub-organization.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$UpdateInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.Name = 'Platform (v2)'
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.Slug = 'platform-v2'
|
||||||
|
$UpdateInfisicalSubOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$UpdateInfisicalSubOrganizationResult = Update-InfisicalSubOrganization @UpdateInfisicalSubOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Renames the sub-organization and updates its slug in a single call.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Remove-InfisicalSubOrganization</command:name>
|
||||||
|
<maml:description><maml:para>Deletes an Infisical sub-organization.</maml:para></maml:description>
|
||||||
|
<command:verb>Remove</command:verb>
|
||||||
|
<command:noun>InfisicalSubOrganization</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Deletes a sub-organization by id. -SubOrganizationId is required. High ConfirmImpact prompts unless -Confirm:$False is supplied. -PassThru emits the removed sub-organization id.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>This is destructive and removes all projects, secrets, and identities scoped to the sub-organization. Honors -WhatIf and -Confirm.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Remove-InfisicalSubOrganization -SubOrganizationId $SubOrganizationId -Confirm:$False</dev:code>
|
||||||
|
<dev:remarks><maml:para>Deletes the supplied sub-organization without prompting.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$RemoveInfisicalSubOrganizationParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.SubOrganizationId = $SubOrganizationId
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.PassThru = $True
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.Confirm = $False
|
||||||
|
$RemoveInfisicalSubOrganizationParameters.Verbose = $True
|
||||||
|
|
||||||
|
$RemoveInfisicalSubOrganizationResult = Remove-InfisicalSubOrganization @RemoveInfisicalSubOrganizationParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Removes the sub-organization without confirmation and emits the removed id for logging.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
|
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
|
||||||
|
<command:details>
|
||||||
|
<command:name>Get-InfisicalSANList</command:name>
|
||||||
|
<maml:description><maml:para>Builds a deduplicated list of Subject Alternative Name candidates for the local device.</maml:para></maml:description>
|
||||||
|
<command:verb>Get</command:verb>
|
||||||
|
<command:noun>InfisicalSANList</command:noun>
|
||||||
|
</command:details>
|
||||||
|
<maml:description>
|
||||||
|
<maml:para>Returns, in order: the local device name; the device name suffixed with each non-empty DNS suffix found on any operational (non-loopback) network adapter and the system primary domain; every IPv4 unicast address whose first octets fall within RFC 1918 (10/8, 172.16/12, 192.168/16) or CGNAT (100.64/10); and the IPv4 and IPv6 loopback addresses (127.0.0.1, ::1). Optional -InclusionExpression and -ExclusionExpression regex filters are applied in that order after collection, before output. Suitable as a one-shot SAN provider for Request-InfisicalCertificate -DnsName.</maml:para>
|
||||||
|
</maml:description>
|
||||||
|
<maml:alertSet>
|
||||||
|
<maml:title>Notes</maml:title>
|
||||||
|
<maml:alert>
|
||||||
|
<maml:para>Output is a single strongly-typed System.String[] array (emitted non-enumerated) so it round-trips into [System.Collections.Generic.List[string]]::AddRange() and binds directly to string[] parameters such as Request-InfisicalCertificate -DnsName. The device name comes first so it can be reused as a CommonName. Routable public IPv4 addresses, link-local addresses, and IPv6 unicast addresses other than loopback are intentionally excluded. -InclusionExpression and -ExclusionExpression are case-insensitive .NET regular expressions; inclusion is applied first, then exclusion.</maml:para>
|
||||||
|
</maml:alert>
|
||||||
|
</maml:alertSet>
|
||||||
|
<command:examples>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 1</maml:title>
|
||||||
|
<dev:code>Get-InfisicalSANList</dev:code>
|
||||||
|
<dev:remarks><maml:para>Returns the SAN candidate list for the current device.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 2</maml:title>
|
||||||
|
<dev:code>$Sans = Get-InfisicalSANList
|
||||||
|
Request-InfisicalCertificate -ProjectId $ProjectId -CertificateAuthorityId $CaId -CommonName $Sans[0] -DnsName $Sans -Ttl '90d'</dev:code>
|
||||||
|
<dev:remarks><maml:para>Captures the SAN list, then uses the device name as the CommonName and the full list as DnsName when requesting a certificate.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
<command:example>
|
||||||
|
<maml:title>EXAMPLE 3</maml:title>
|
||||||
|
<dev:code>$GetInfisicalSANListParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$GetInfisicalSANListParameters.InclusionExpression = '\.gracesolution\.prv$|^10\.|^172\.'
|
||||||
|
$GetInfisicalSANListParameters.ExclusionExpression = '^127\.|^::1$'
|
||||||
|
$Sans = Get-InfisicalSANList @GetInfisicalSANListParameters</dev:code>
|
||||||
|
<dev:remarks><maml:para>Keeps only entries ending in the corporate DNS suffix or sitting in the 10/8 or 172/12 ranges, then drops loopback. Filters are case-insensitive and applied in fetch -> include -> exclude -> output order.</maml:para></dev:remarks>
|
||||||
|
</command:example>
|
||||||
|
</command:examples>
|
||||||
|
</command:command>
|
||||||
|
|
||||||
</helpItems>
|
</helpItems>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Import-Module -Name .\Module\PSInfisicalAPI
|
|||||||
|
|
||||||
## Cmdlets
|
## Cmdlets
|
||||||
|
|
||||||
The module exports 42 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` (default) / single-record parameter-set pair: invoking without the identity parameter returns the collection, supplying the identity parameter returns one record.
|
The module exports 51 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List` (default) / single-record parameter-set pair: invoking without the identity parameter returns the collection, supplying the identity parameter returns one record.
|
||||||
|
|
||||||
### Session
|
### Session
|
||||||
|
|
||||||
@@ -47,6 +47,24 @@ The module exports 42 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List`
|
|||||||
| `ConvertTo-InfisicalSecretDictionary` | Converts a stream of InfisicalSecret objects into a name-keyed Dictionary of SecureString or plain text values. |
|
| `ConvertTo-InfisicalSecretDictionary` | Converts a stream of InfisicalSecret objects into a name-keyed Dictionary of SecureString or plain text values. |
|
||||||
| `Export-InfisicalSecrets` | Exports InfisicalSecret objects to disk or environment variables in a chosen file format. |
|
| `Export-InfisicalSecrets` | Exports InfisicalSecret objects to disk or environment variables in a chosen file format. |
|
||||||
|
|
||||||
|
### Organizations
|
||||||
|
|
||||||
|
| Cmdlet | Purpose |
|
||||||
|
| ------------------------------ | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| `Get-InfisicalOrganization` | Lists or retrieves Infisical organizations accessible to the current identity. |
|
||||||
|
| `New-InfisicalOrganization` | Creates a new Infisical organization. |
|
||||||
|
| `Update-InfisicalOrganization` | Updates the name or slug of an existing Infisical organization. |
|
||||||
|
| `Remove-InfisicalOrganization` | Deletes an Infisical organization. |
|
||||||
|
|
||||||
|
### Sub-Organizations
|
||||||
|
|
||||||
|
| Cmdlet | Purpose |
|
||||||
|
| --------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| `Get-InfisicalSubOrganization` | Lists or retrieves Infisical sub-organizations, with optional search, paging, and ordering filters. |
|
||||||
|
| `New-InfisicalSubOrganization` | Creates a new Infisical sub-organization. |
|
||||||
|
| `Update-InfisicalSubOrganization` | Updates the name or slug of an existing Infisical sub-organization. |
|
||||||
|
| `Remove-InfisicalSubOrganization` | Deletes an Infisical sub-organization. |
|
||||||
|
|
||||||
### Projects
|
### Projects
|
||||||
|
|
||||||
| Cmdlet | Purpose |
|
| Cmdlet | Purpose |
|
||||||
@@ -98,6 +116,7 @@ The module exports 42 cmdlets. Discovery cmdlets (`Get-Infisical*`) use a `List`
|
|||||||
| `Get-InfisicalScepMdmProfile` | Projects an Infisical certificate profile into a Windows SCEP MDM profile model. |
|
| `Get-InfisicalScepMdmProfile` | Projects an Infisical certificate profile into a Windows SCEP MDM profile model. |
|
||||||
| `Export-InfisicalScepMdmProfile` | Writes a SCEP MDM profile to disk as a SyncML payload suitable for MDM delivery. |
|
| `Export-InfisicalScepMdmProfile` | Writes a SCEP MDM profile to disk as a SyncML payload suitable for MDM delivery. |
|
||||||
| `Write-InfisicalScepMdmProfileToWmi`| Submits a SCEP MDM profile to the local MDM Bridge WMI provider to trigger enrollment. |
|
| `Write-InfisicalScepMdmProfileToWmi`| Submits a SCEP MDM profile to the local MDM Bridge WMI provider to trigger enrollment. |
|
||||||
|
| `Get-InfisicalSANList` | Builds a SAN candidate list (device name, `<device>.<suffix>` per adapter DNS suffix, RFC 1918 + CGNAT IPv4 addresses, IPv4/IPv6 loopback) for `Request-InfisicalCertificate -DnsName`. |
|
||||||
|
|
||||||
### Process
|
### Process
|
||||||
|
|
||||||
@@ -125,6 +144,40 @@ Get-InfisicalSecret -SecretPath '/'
|
|||||||
Disconnect-Infisical
|
Disconnect-Infisical
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## End-to-end: request and install a chained certificate
|
||||||
|
|
||||||
|
Connects, selects a project by name, sources SANs from `Get-InfisicalSANList`, picks the first available internal CA, requests a certificate, installs it (and its chain) into the current-user store, and disconnects. Each call uses a splatted `OrderedDictionary` constructed with `OrdinalIgnoreCase` so parameter names round-trip case-insensitively.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$ConnectInfisicalParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$ConnectInfisicalParameters.BaseUri = 'https://app.infisical.com'
|
||||||
|
$ConnectInfisicalParameters.OrganizationId = '00000000-0000-0000-0000-000000000000'
|
||||||
|
$ConnectInfisicalParameters.ClientId = 'machine-identity-client-id'
|
||||||
|
$ConnectInfisicalParameters.ClientSecret = ConvertTo-SecureString -String 'ClientSecret' -AsPlainText -Force
|
||||||
|
$ConnectInfisicalParameters.PassThru = $True
|
||||||
|
$ConnectInfisicalParameters.Verbose = $True
|
||||||
|
|
||||||
|
$Connection = Connect-Infisical @ConnectInfisicalParameters
|
||||||
|
|
||||||
|
$Project = Get-InfisicalProject | Where-Object {($_.Name -eq 'Platform')} | Select-Object -First 1
|
||||||
|
$Ca = Get-InfisicalCertificateAuthority -ProjectId ($Project.Id) | Select-Object -First 1
|
||||||
|
$SanList = Get-InfisicalSANList
|
||||||
|
|
||||||
|
$RequestInfisicalCertificateParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' -ArgumentList ([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$RequestInfisicalCertificateParameters.ProjectId = $Project.Id
|
||||||
|
$RequestInfisicalCertificateParameters.CertificateAuthorityId = $Ca.Id
|
||||||
|
$RequestInfisicalCertificateParameters.CommonName = "CN=$($Env:ComputerName.ToUpper())"
|
||||||
|
$RequestInfisicalCertificateParameters.DnsName = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
||||||
|
$RequestInfisicalCertificateParameters.DnsName.AddRange($SanList)
|
||||||
|
$RequestInfisicalCertificateParameters.DnsName.Add('myrecord.mydomain.com')
|
||||||
|
$RequestInfisicalCertificateParameters.Ttl = '90d'
|
||||||
|
$RequestInfisicalCertificateParameters.Install = $True
|
||||||
|
$RequestInfisicalCertificateParameters.InstallChain = $True
|
||||||
|
$Certificate = Request-InfisicalCertificate @RequestInfisicalCertificateParameters
|
||||||
|
|
||||||
|
$Null = Disconnect-Infisical -Verbose
|
||||||
|
```
|
||||||
|
|
||||||
## Automatic environment-variable discovery
|
## Automatic environment-variable discovery
|
||||||
|
|
||||||
When `Connect-Infisical` is invoked with one or more parameters missing (or set to whitespace/empty), the cmdlet searches environment variables and uses the first value it finds. This makes invocation as simple as `Connect-Infisical` when variables are set up in advance.
|
When `Connect-Infisical` is invoked with one or more parameters missing (or set to whitespace/empty), the cmdlet searches environment variables and uses the first value it finds. This makes invocation as simple as `Connect-Infisical` when variables are set up in advance.
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ function Write-Manifest {
|
|||||||
'Copy-InfisicalSecret',
|
'Copy-InfisicalSecret',
|
||||||
'ConvertTo-InfisicalSecretDictionary',
|
'ConvertTo-InfisicalSecretDictionary',
|
||||||
'Export-InfisicalSecrets',
|
'Export-InfisicalSecrets',
|
||||||
|
'Import-InfisicalSecret',
|
||||||
'Get-InfisicalProject',
|
'Get-InfisicalProject',
|
||||||
'New-InfisicalProject',
|
'New-InfisicalProject',
|
||||||
'Update-InfisicalProject',
|
'Update-InfisicalProject',
|
||||||
@@ -129,6 +130,14 @@ function Write-Manifest {
|
|||||||
'New-InfisicalTag',
|
'New-InfisicalTag',
|
||||||
'Update-InfisicalTag',
|
'Update-InfisicalTag',
|
||||||
'Remove-InfisicalTag',
|
'Remove-InfisicalTag',
|
||||||
|
'Get-InfisicalOrganization',
|
||||||
|
'New-InfisicalOrganization',
|
||||||
|
'Update-InfisicalOrganization',
|
||||||
|
'Remove-InfisicalOrganization',
|
||||||
|
'Get-InfisicalSubOrganization',
|
||||||
|
'New-InfisicalSubOrganization',
|
||||||
|
'Update-InfisicalSubOrganization',
|
||||||
|
'Remove-InfisicalSubOrganization',
|
||||||
'Get-InfisicalCertificateAuthority',
|
'Get-InfisicalCertificateAuthority',
|
||||||
'Get-InfisicalPkiSubscriber',
|
'Get-InfisicalPkiSubscriber',
|
||||||
'Get-InfisicalCertificateProfile',
|
'Get-InfisicalCertificateProfile',
|
||||||
@@ -145,7 +154,9 @@ function Write-Manifest {
|
|||||||
'Get-InfisicalScepMdmProfile',
|
'Get-InfisicalScepMdmProfile',
|
||||||
'Export-InfisicalScepMdmProfile',
|
'Export-InfisicalScepMdmProfile',
|
||||||
'Write-InfisicalScepMdmProfileToWmi',
|
'Write-InfisicalScepMdmProfileToWmi',
|
||||||
'Start-InfisicalProcess'
|
'Start-InfisicalProcess',
|
||||||
|
'Get-InfisicalEnvironmentVariable',
|
||||||
|
'Get-InfisicalSANList'
|
||||||
)
|
)
|
||||||
AliasesToExport = @()
|
AliasesToExport = @()
|
||||||
VariablesToExport = @()
|
VariablesToExport = @()
|
||||||
@@ -210,7 +221,7 @@ if (`$cmds.Count -eq 0) {
|
|||||||
throw "No cmdlets were exported by the PSInfisicalAPI module."
|
throw "No cmdlets were exported by the PSInfisicalAPI module."
|
||||||
}
|
}
|
||||||
|
|
||||||
`$expectedCmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','Copy-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag','Get-InfisicalCertificateAuthority','Get-InfisicalPkiSubscriber','Get-InfisicalCertificateProfile','Get-InfisicalCertificatePolicy','Get-InfisicalCertificate','Request-InfisicalCertificate','ConvertTo-InfisicalCertificate','Install-InfisicalCertificate','Uninstall-InfisicalCertificate','Export-InfisicalCertificate','Get-InfisicalCertificateApplication','Get-InfisicalCertificateApplicationEnrollment','New-InfisicalScepDynamicChallenge','Get-InfisicalScepMdmProfile','Export-InfisicalScepMdmProfile','Write-InfisicalScepMdmProfileToWmi','Start-InfisicalProcess')
|
`$expectedCmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','Copy-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Import-InfisicalSecret','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag','Get-InfisicalOrganization','New-InfisicalOrganization','Update-InfisicalOrganization','Remove-InfisicalOrganization','Get-InfisicalSubOrganization','New-InfisicalSubOrganization','Update-InfisicalSubOrganization','Remove-InfisicalSubOrganization','Get-InfisicalCertificateAuthority','Get-InfisicalPkiSubscriber','Get-InfisicalCertificateProfile','Get-InfisicalCertificatePolicy','Get-InfisicalCertificate','Request-InfisicalCertificate','ConvertTo-InfisicalCertificate','Install-InfisicalCertificate','Uninstall-InfisicalCertificate','Export-InfisicalCertificate','Get-InfisicalCertificateApplication','Get-InfisicalCertificateApplicationEnrollment','New-InfisicalScepDynamicChallenge','Get-InfisicalScepMdmProfile','Export-InfisicalScepMdmProfile','Write-InfisicalScepMdmProfileToWmi','Start-InfisicalProcess','Get-InfisicalEnvironmentVariable','Get-InfisicalSANList')
|
||||||
foreach (`$expected in `$expectedCmds) {
|
foreach (`$expected in `$expectedCmds) {
|
||||||
if (-not (Get-Command -Name `$expected -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) {
|
if (-not (Get-Command -Name `$expected -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) {
|
||||||
throw "Cmdlet not found: `$expected"
|
throw "Cmdlet not found: `$expected"
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ Get-InfisicalTag
|
|||||||
New-InfisicalTag
|
New-InfisicalTag
|
||||||
Update-InfisicalTag
|
Update-InfisicalTag
|
||||||
Remove-InfisicalTag
|
Remove-InfisicalTag
|
||||||
|
Get-InfisicalOrganization
|
||||||
|
New-InfisicalOrganization
|
||||||
|
Update-InfisicalOrganization
|
||||||
|
Remove-InfisicalOrganization
|
||||||
|
Get-InfisicalSubOrganization
|
||||||
|
New-InfisicalSubOrganization
|
||||||
|
Update-InfisicalSubOrganization
|
||||||
|
Remove-InfisicalSubOrganization
|
||||||
```
|
```
|
||||||
|
|
||||||
Infisical’s public API is REST-based and provides programmatic access for managing secrets and related resources. Current Infisical documentation shows the list-secrets endpoint under `/api/v4/secrets`, the single-secret retrieval endpoint under `/api/v4/secrets/{secretName}`, and Universal Auth login under `/api/v1/auth/universal-auth/login`. The implementation must centralize API endpoint definitions because Infisical uses different API versions across resource families. ([Infisical Blog][1])
|
Infisical’s public API is REST-based and provides programmatic access for managing secrets and related resources. Current Infisical documentation shows the list-secrets endpoint under `/api/v4/secrets`, the single-secret retrieval endpoint under `/api/v4/secrets/{secretName}`, and Universal Auth login under `/api/v1/auth/universal-auth/login`. The implementation must centralize API endpoint definitions because Infisical uses different API versions across resource families. ([Infisical Blog][1])
|
||||||
@@ -1214,7 +1222,8 @@ Export-InfisicalSecrets `
|
|||||||
[-Scope <Process|User|Machine>] `
|
[-Scope <Process|User|Machine>] `
|
||||||
[-Force] `
|
[-Force] `
|
||||||
[-Encoding <UTF8|UTF8Bom|Unicode>] `
|
[-Encoding <UTF8|UTF8Bom|Unicode>] `
|
||||||
[-Prefix <string>]
|
[-SecretsPrefix <string>] `
|
||||||
|
[-ForceSecretsPrefix]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameter Rules
|
## Parameter Rules
|
||||||
@@ -1513,8 +1522,9 @@ Start-InfisicalProcess
|
|||||||
[-SecureArgumentList]
|
[-SecureArgumentList]
|
||||||
[-LogOutput]
|
[-LogOutput]
|
||||||
[-ContinueOnError]
|
[-ContinueOnError]
|
||||||
[-Secret <InfisicalSecret[]>]
|
[-Secrets <InfisicalSecret[]>]
|
||||||
[-Prefix <string>]
|
[-SecretsPrefix <string>]
|
||||||
|
[-ForceSecretsPrefix]
|
||||||
```
|
```
|
||||||
|
|
||||||
Behavior:
|
Behavior:
|
||||||
@@ -1522,7 +1532,7 @@ Behavior:
|
|||||||
```text
|
```text
|
||||||
Buffer pipeline InfisicalSecret objects in ProcessRecord.
|
Buffer pipeline InfisicalSecret objects in ProcessRecord.
|
||||||
Decrypt secrets only into ProcessStartInfo.Environment.
|
Decrypt secrets only into ProcessStartInfo.Environment.
|
||||||
Apply -Prefix to each secret name before injection.
|
Apply -SecretsPrefix to each secret name before injection.
|
||||||
Never write secret plaintext to user or machine environment scope.
|
Never write secret plaintext to user or machine environment scope.
|
||||||
Honor -WhatIf / -Confirm.
|
Honor -WhatIf / -Confirm.
|
||||||
Default -AcceptableExitCodeList = @('0','3010').
|
Default -AcceptableExitCodeList = @('0','3010').
|
||||||
@@ -1533,6 +1543,78 @@ Output: `InfisicalProcessResult` with `ExitCode`, `ExitCodeAsHex`, `ExitCodeAsIn
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# 16.7 Organization Cmdlets
|
||||||
|
|
||||||
|
Organizations are the top-level tenancy boundary in Infisical. They are not scoped under a project; the active connection's `OrganizationId` is used as the default identifier when an explicit one is not supplied.
|
||||||
|
|
||||||
|
Cmdlet signatures:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-InfisicalOrganization [[-OrganizationId] <string>] # default = List
|
||||||
|
New-InfisicalOrganization [-Name] <string> [-Slug <string>] [-WhatIf] [-Confirm]
|
||||||
|
Update-InfisicalOrganization [-OrganizationId] <string> [-Name <string>] [-Slug <string>] [-WhatIf] [-Confirm]
|
||||||
|
Remove-InfisicalOrganization [-OrganizationId] <string> [-PassThru] [-WhatIf] [-Confirm]
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameter sets:
|
||||||
|
|
||||||
|
| Cmdlet | Default set | Single set | Notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `Get-InfisicalOrganization` | `List` (no `-Id`) | `Single` (`-OrganizationId`/`-Id`) | No `-ProjectId`. |
|
||||||
|
| `New-InfisicalOrganization` | n/a | `-Name` mandatory, `-Slug` optional | ShouldProcess. |
|
||||||
|
| `Update-InfisicalOrganization` | n/a | `-OrganizationId` mandatory | ShouldProcess; only bound parameters are sent. |
|
||||||
|
| `Remove-InfisicalOrganization` | n/a | `-OrganizationId` mandatory | `ConfirmImpact.High`; `-PassThru` emits removed id. |
|
||||||
|
|
||||||
|
Endpoints:
|
||||||
|
|
||||||
|
| Operation | Method | Template | Version |
|
||||||
|
|---|---|---|---|
|
||||||
|
| List | `GET` | `/api/v2/organizations` | v2 |
|
||||||
|
| Retrieve | `GET` | `/api/v1/organization/{organizationId}` | v1 |
|
||||||
|
| Create | `POST` | `/api/v2/organizations` | v2 |
|
||||||
|
| Update | `PATCH` | `/api/v1/organization/{organizationId}` | v1 |
|
||||||
|
| Delete | `DELETE` | `/api/v1/organization/{organizationId}` | v1 |
|
||||||
|
|
||||||
|
Output: `InfisicalOrganization` with `Id`, `Name`, `Slug`, `CustomerId`, `AuthEnforced`, `ScimEnabled`, `CreatedAtUtc`, `UpdatedAtUtc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 16.8 Sub-Organization Cmdlets
|
||||||
|
|
||||||
|
Sub-organizations partition an organization into isolated child tenants. They are not scoped under a project; the active connection is used for the parent organization context.
|
||||||
|
|
||||||
|
Cmdlet signatures:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-InfisicalSubOrganization [[-SubOrganizationId] <string>] [-Limit <int>] [-Offset <int>] [-Search <string>] [-OrderBy <string>] [-OrderDirection <string>] [-IsAccessible]
|
||||||
|
New-InfisicalSubOrganization [-Name] <string> [-Slug] <string> [-WhatIf] [-Confirm]
|
||||||
|
Update-InfisicalSubOrganization [-SubOrganizationId] <string> [-Name <string>] [-Slug <string>] [-WhatIf] [-Confirm]
|
||||||
|
Remove-InfisicalSubOrganization [-SubOrganizationId] <string> [-PassThru] [-WhatIf] [-Confirm]
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameter sets:
|
||||||
|
|
||||||
|
| Cmdlet | Default set | Single set | Notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `Get-InfisicalSubOrganization` | `List` (no `-Id`) | `Single` (`-SubOrganizationId`/`-Id`) | List supports server-side `-Limit`, `-Offset`, `-Search`, `-OrderBy`, `-OrderDirection`, `-IsAccessible`. |
|
||||||
|
| `New-InfisicalSubOrganization` | n/a | `-Name` + `-Slug` mandatory | ShouldProcess. |
|
||||||
|
| `Update-InfisicalSubOrganization` | n/a | `-SubOrganizationId` mandatory | ShouldProcess; only bound parameters are sent. |
|
||||||
|
| `Remove-InfisicalSubOrganization` | n/a | `-SubOrganizationId` mandatory | `ConfirmImpact.High`; `-PassThru` emits removed id. |
|
||||||
|
|
||||||
|
Endpoints (beta):
|
||||||
|
|
||||||
|
| Operation | Method | Template | Version |
|
||||||
|
|---|---|---|---|
|
||||||
|
| List | `GET` | `/api/v1/sub-organizations` | v1 |
|
||||||
|
| Retrieve | `GET` | `/api/v1/sub-organizations/{subOrgId}` | v1 |
|
||||||
|
| Create | `POST` | `/api/v1/sub-organizations` | v1 |
|
||||||
|
| Update | `PATCH` | `/api/v1/sub-organizations/{subOrgId}` | v1 |
|
||||||
|
| Delete | `DELETE` | `/api/v1/sub-organizations/{subOrgId}` | v1 |
|
||||||
|
|
||||||
|
Output: `InfisicalSubOrganization` with `Id`, `Name`, `Slug`, `OrganizationId`, `IsAccessible`, `CreatedAtUtc`, `UpdatedAtUtc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 17. SecureString Utility
|
# 17. SecureString Utility
|
||||||
|
|
||||||
Required utility:
|
Required utility:
|
||||||
@@ -75,6 +75,16 @@ namespace PSInfisicalAPI.Tests
|
|||||||
[InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v4/secrets/batch")]
|
[InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v4/secrets/batch")]
|
||||||
[InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v4/secrets/batch")]
|
[InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v4/secrets/batch")]
|
||||||
[InlineData(InfisicalEndpointNames.DuplicateSecret, "POST", "/api/v4/secrets/duplicate")]
|
[InlineData(InfisicalEndpointNames.DuplicateSecret, "POST", "/api/v4/secrets/duplicate")]
|
||||||
|
[InlineData(InfisicalEndpointNames.ListOrganizations, "GET", "/api/v2/organizations")]
|
||||||
|
[InlineData(InfisicalEndpointNames.RetrieveOrganization, "GET", "/api/v1/organization/{organizationId}")]
|
||||||
|
[InlineData(InfisicalEndpointNames.CreateOrganization, "POST", "/api/v2/organizations")]
|
||||||
|
[InlineData(InfisicalEndpointNames.UpdateOrganization, "PATCH", "/api/v1/organization/{organizationId}")]
|
||||||
|
[InlineData(InfisicalEndpointNames.DeleteOrganization, "DELETE", "/api/v1/organization/{organizationId}")]
|
||||||
|
[InlineData(InfisicalEndpointNames.ListSubOrganizations, "GET", "/api/v1/sub-organizations")]
|
||||||
|
[InlineData(InfisicalEndpointNames.RetrieveSubOrganization, "GET", "/api/v1/sub-organizations/{subOrgId}")]
|
||||||
|
[InlineData(InfisicalEndpointNames.CreateSubOrganization, "POST", "/api/v1/sub-organizations")]
|
||||||
|
[InlineData(InfisicalEndpointNames.UpdateSubOrganization, "PATCH", "/api/v1/sub-organizations/{subOrgId}")]
|
||||||
|
[InlineData(InfisicalEndpointNames.DeleteSubOrganization, "DELETE", "/api/v1/sub-organizations/{subOrgId}")]
|
||||||
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
|
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
|
||||||
{
|
{
|
||||||
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name);
|
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name);
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Tests
|
||||||
|
{
|
||||||
|
public class OrganizationMapperTests
|
||||||
|
{
|
||||||
|
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||||
|
.GetType("PSInfisicalAPI.Organizations.InfisicalOrganizationMapper", true);
|
||||||
|
|
||||||
|
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||||
|
.GetType("PSInfisicalAPI.Organizations.InfisicalOrganizationResponseDto", true);
|
||||||
|
|
||||||
|
private static InfisicalOrganization InvokeMap(object dto)
|
||||||
|
{
|
||||||
|
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
return (InfisicalOrganization)map.Invoke(null, new[] { dto });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Null_Dto_Returns_Null()
|
||||||
|
{
|
||||||
|
Assert.Null(InvokeMap(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Populates_Core_Fields()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("Id").SetValue(dto, "org-001");
|
||||||
|
DtoType.GetProperty("Name").SetValue(dto, "Acme");
|
||||||
|
DtoType.GetProperty("Slug").SetValue(dto, "acme");
|
||||||
|
DtoType.GetProperty("CustomerId").SetValue(dto, "cust-9");
|
||||||
|
DtoType.GetProperty("AuthEnforced").SetValue(dto, true);
|
||||||
|
DtoType.GetProperty("ScimEnabled").SetValue(dto, true);
|
||||||
|
DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z");
|
||||||
|
DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z");
|
||||||
|
|
||||||
|
InfisicalOrganization organization = InvokeMap(dto);
|
||||||
|
|
||||||
|
Assert.Equal("org-001", organization.Id);
|
||||||
|
Assert.Equal("Acme", organization.Name);
|
||||||
|
Assert.Equal("acme", organization.Slug);
|
||||||
|
Assert.Equal("cust-9", organization.CustomerId);
|
||||||
|
Assert.True(organization.AuthEnforced);
|
||||||
|
Assert.True(organization.ScimEnabled);
|
||||||
|
Assert.NotNull(organization.CreatedAtUtc);
|
||||||
|
Assert.NotNull(organization.UpdatedAtUtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Falls_Back_To_InternalId()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1");
|
||||||
|
|
||||||
|
InfisicalOrganization organization = InvokeMap(dto);
|
||||||
|
|
||||||
|
Assert.Equal("internal-id-1", organization.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapMany_Null_Returns_Empty()
|
||||||
|
{
|
||||||
|
MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
InfisicalOrganization[] result = (InfisicalOrganization[])mapMany.Invoke(null, new object[] { null });
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Tests
|
||||||
|
{
|
||||||
|
public class SubOrganizationMapperTests
|
||||||
|
{
|
||||||
|
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||||
|
.GetType("PSInfisicalAPI.SubOrganizations.InfisicalSubOrganizationMapper", true);
|
||||||
|
|
||||||
|
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||||
|
.GetType("PSInfisicalAPI.SubOrganizations.InfisicalSubOrganizationResponseDto", true);
|
||||||
|
|
||||||
|
private static InfisicalSubOrganization InvokeMap(object dto)
|
||||||
|
{
|
||||||
|
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
return (InfisicalSubOrganization)map.Invoke(null, new[] { dto });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Null_Dto_Returns_Null()
|
||||||
|
{
|
||||||
|
Assert.Null(InvokeMap(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Populates_Core_Fields()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("Id").SetValue(dto, "sub-001");
|
||||||
|
DtoType.GetProperty("Name").SetValue(dto, "Platform Engineering");
|
||||||
|
DtoType.GetProperty("Slug").SetValue(dto, "platform-eng");
|
||||||
|
DtoType.GetProperty("OrganizationId").SetValue(dto, "org-001");
|
||||||
|
DtoType.GetProperty("IsAccessible").SetValue(dto, true);
|
||||||
|
DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z");
|
||||||
|
DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z");
|
||||||
|
|
||||||
|
InfisicalSubOrganization subOrganization = InvokeMap(dto);
|
||||||
|
|
||||||
|
Assert.Equal("sub-001", subOrganization.Id);
|
||||||
|
Assert.Equal("Platform Engineering", subOrganization.Name);
|
||||||
|
Assert.Equal("platform-eng", subOrganization.Slug);
|
||||||
|
Assert.Equal("org-001", subOrganization.OrganizationId);
|
||||||
|
Assert.True(subOrganization.IsAccessible);
|
||||||
|
Assert.NotNull(subOrganization.CreatedAtUtc);
|
||||||
|
Assert.NotNull(subOrganization.UpdatedAtUtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Falls_Back_To_InternalId_And_OrgId()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1");
|
||||||
|
DtoType.GetProperty("OrgId").SetValue(dto, "org-fallback");
|
||||||
|
|
||||||
|
InfisicalSubOrganization subOrganization = InvokeMap(dto);
|
||||||
|
|
||||||
|
Assert.Equal("internal-id-1", subOrganization.Id);
|
||||||
|
Assert.Equal("org-fallback", subOrganization.OrganizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapMany_Null_Returns_Empty()
|
||||||
|
{
|
||||||
|
MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
InfisicalSubOrganization[] result = (InfisicalSubOrganization[])mapMany.Invoke(null, new object[] { null });
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
using PSInfisicalAPI.Common;
|
||||||
using PSInfisicalAPI.Errors;
|
using PSInfisicalAPI.Errors;
|
||||||
using PSInfisicalAPI.Models;
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
@@ -21,6 +22,14 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public SwitchParameter AsPlainText { get; set; }
|
public SwitchParameter AsPlainText { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Alias("Prefix")]
|
||||||
|
public string SecretsPrefix { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Alias("ForcePrefix")]
|
||||||
|
public SwitchParameter ForceSecretsPrefix { get; set; }
|
||||||
|
|
||||||
private readonly List<InfisicalSecret> _buffer = new List<InfisicalSecret>();
|
private readonly List<InfisicalSecret> _buffer = new List<InfisicalSecret>();
|
||||||
|
|
||||||
protected override void ProcessRecord()
|
protected override void ProcessRecord()
|
||||||
@@ -40,14 +49,18 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Logger.Information("ConvertTo-InfisicalSecretDictionary", string.Concat("Processing ", _buffer.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " input secret(s)."));
|
||||||
|
|
||||||
if (AsPlainText.IsPresent)
|
if (AsPlainText.IsPresent)
|
||||||
{
|
{
|
||||||
Dictionary<string, string> plain = BuildDictionary<string>(secret => secret.GetPlainTextValue());
|
Dictionary<string, string> plain = BuildDictionary<string>(secret => secret.GetPlainTextValue());
|
||||||
|
Logger.Information("ConvertTo-InfisicalSecretDictionary", string.Concat("Built plain-text dictionary with ", plain.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " entry/entries."));
|
||||||
WriteObject(plain);
|
WriteObject(plain);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dictionary<string, SecureString> secure = BuildDictionary<SecureString>(secret => secret.SecretValue);
|
Dictionary<string, SecureString> secure = BuildDictionary<SecureString>(secret => secret.SecretValue);
|
||||||
|
Logger.Information("ConvertTo-InfisicalSecretDictionary", string.Concat("Built SecureString dictionary with ", secure.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " entry/entries."));
|
||||||
WriteObject(secure);
|
WriteObject(secure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +76,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
|
|
||||||
foreach (InfisicalSecret secret in _buffer)
|
foreach (InfisicalSecret secret in _buffer)
|
||||||
{
|
{
|
||||||
string key = secret.SecretName ?? string.Empty;
|
string key = InfisicalPrefix.Apply(secret.SecretName ?? string.Empty, SecretsPrefix, ForceSecretsPrefix.IsPresent);
|
||||||
|
|
||||||
if (dictionary.ContainsKey(key))
|
if (dictionary.ContainsKey(key))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using PSInfisicalAPI.Common;
|
||||||
using PSInfisicalAPI.Errors;
|
using PSInfisicalAPI.Errors;
|
||||||
using PSInfisicalAPI.Exports;
|
using PSInfisicalAPI.Exports;
|
||||||
using PSInfisicalAPI.Models;
|
using PSInfisicalAPI.Models;
|
||||||
@@ -38,7 +39,12 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
public InfisicalExportEncoding Encoding { get; set; } = InfisicalExportEncoding.UTF8;
|
public InfisicalExportEncoding Encoding { get; set; } = InfisicalExportEncoding.UTF8;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Prefix { get; set; }
|
[Alias("Prefix")]
|
||||||
|
public string SecretsPrefix { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Alias("ForcePrefix")]
|
||||||
|
public SwitchParameter ForceSecretsPrefix { get; set; }
|
||||||
|
|
||||||
private readonly List<InfisicalSecret> _buffer = new List<InfisicalSecret>();
|
private readonly List<InfisicalSecret> _buffer = new List<InfisicalSecret>();
|
||||||
|
|
||||||
@@ -69,9 +75,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Information("Export-InfisicalSecrets", string.Concat("Exporting ", _buffer.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " secret(s) as ", Format.ToString(), (Path != null ? string.Concat(" to '", Path.FullName, "'") : string.Empty), "."));
|
||||||
|
|
||||||
InfisicalExportRequest request = new InfisicalExportRequest
|
InfisicalExportRequest request = new InfisicalExportRequest
|
||||||
{
|
{
|
||||||
Secrets = ApplyPrefix(_buffer, Prefix),
|
Secrets = ApplySecretsPrefix(_buffer, SecretsPrefix, ForceSecretsPrefix.IsPresent),
|
||||||
Format = Format,
|
Format = Format,
|
||||||
Path = Path,
|
Path = Path,
|
||||||
Scope = Scope,
|
Scope = Scope,
|
||||||
@@ -88,7 +96,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InfisicalSecret[] ApplyPrefix(List<InfisicalSecret> source, string prefix)
|
private static InfisicalSecret[] ApplySecretsPrefix(List<InfisicalSecret> source, string prefix, bool force)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(prefix)) { return source.ToArray(); }
|
if (string.IsNullOrEmpty(prefix)) { return source.ToArray(); }
|
||||||
|
|
||||||
@@ -104,7 +112,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
Environment = original.Environment,
|
Environment = original.Environment,
|
||||||
Version = original.Version,
|
Version = original.Version,
|
||||||
Type = original.Type,
|
Type = original.Type,
|
||||||
SecretName = string.Concat(prefix, original.SecretName),
|
SecretName = InfisicalPrefix.Apply(original.SecretName, prefix, force),
|
||||||
SecretValue = original.SecretValue,
|
SecretValue = original.SecretValue,
|
||||||
SecretValueHidden = original.SecretValueHidden,
|
SecretValueHidden = original.SecretValueHidden,
|
||||||
SecretPath = original.SecretPath,
|
SecretPath = original.SecretPath,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalCertificateApplication[] all = client.ListCertificateApplications(connection, ProjectId, Limit, Offset);
|
InfisicalCertificateApplication[] all = client.ListCertificateApplications(connection, ProjectId, Limit, Offset);
|
||||||
|
Logger.Information("Get-InfisicalCertificateApplication", string.Concat("Returned ", all.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " certificate application(s)."));
|
||||||
foreach (InfisicalCertificateApplication app in all)
|
foreach (InfisicalCertificateApplication app in all)
|
||||||
{
|
{
|
||||||
WriteObject(app);
|
WriteObject(app);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Information("Get-InfisicalCertificateAuthority", string.Concat("Returned ", all.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " certificate authority/authorities (kind=", Kind, ")."));
|
||||||
foreach (InfisicalCertificateAuthority ca in all)
|
foreach (InfisicalCertificateAuthority ca in all)
|
||||||
{
|
{
|
||||||
WriteObject(ca);
|
WriteObject(ca);
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
|
|
||||||
query.Offset = (query.Offset ?? 0) + page.Certificates.Length;
|
query.Offset = (query.Offset ?? 0) + page.Certificates.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Information("Get-InfisicalCertificate", string.Concat("Returned ", emitted.ToString(System.Globalization.CultureInfo.InvariantCulture), " certificate(s)."));
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalCertificatePolicy[] all = client.ListCertificatePolicies(connection, ProjectId, Limit, Offset);
|
InfisicalCertificatePolicy[] all = client.ListCertificatePolicies(connection, ProjectId, Limit, Offset);
|
||||||
|
Logger.Information("Get-InfisicalCertificatePolicy", string.Concat("Returned ", all.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " certificate policy/policies."));
|
||||||
foreach (InfisicalCertificatePolicy policy in all)
|
foreach (InfisicalCertificatePolicy policy in all)
|
||||||
{
|
{
|
||||||
WriteObject(policy);
|
WriteObject(policy);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
|
|
||||||
bool? includeConfigs = MyInvocation.BoundParameters.ContainsKey("IncludeConfigs") ? (bool?)IncludeConfigs.IsPresent : null;
|
bool? includeConfigs = MyInvocation.BoundParameters.ContainsKey("IncludeConfigs") ? (bool?)IncludeConfigs.IsPresent : null;
|
||||||
InfisicalCertificateProfile[] all = client.ListCertificateProfiles(connection, ProjectId, Limit, Offset, includeConfigs);
|
InfisicalCertificateProfile[] all = client.ListCertificateProfiles(connection, ProjectId, Limit, Offset, includeConfigs);
|
||||||
|
Logger.Information("Get-InfisicalCertificateProfile", string.Concat("Returned ", all.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " certificate profile(s)."));
|
||||||
foreach (InfisicalCertificateProfile profile in all)
|
foreach (InfisicalCertificateProfile profile in all)
|
||||||
{
|
{
|
||||||
WriteObject(profile);
|
WriteObject(profile);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalEnvironment[] envs = client.List(connection, ProjectId);
|
InfisicalEnvironment[] envs = client.List(connection, ProjectId);
|
||||||
|
Logger.Information("Get-InfisicalEnvironment", string.Concat("Returned ", envs.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " environment(s)."));
|
||||||
foreach (InfisicalEnvironment env in envs)
|
foreach (InfisicalEnvironment env in envs)
|
||||||
{
|
{
|
||||||
WriteObject(env);
|
WriteObject(env);
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Get, "InfisicalEnvironmentVariable")]
|
||||||
|
[OutputType(typeof(string))]
|
||||||
|
public sealed class GetInfisicalEnvironmentVariableCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
private const string Component = "Get-InfisicalEnvironmentVariable";
|
||||||
|
|
||||||
|
private static readonly EnvironmentVariableTarget[] TargetOrder = new[]
|
||||||
|
{
|
||||||
|
EnvironmentVariableTarget.Process,
|
||||||
|
EnvironmentVariableTarget.User,
|
||||||
|
EnvironmentVariableTarget.Machine
|
||||||
|
};
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
|
||||||
|
[ValidateNotNullOrEmpty]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Position = 1)]
|
||||||
|
public EnvironmentVariableTarget? Scope { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
EnvironmentVariableTarget[] targets = Scope.HasValue ? new[] { Scope.Value } : TargetOrder;
|
||||||
|
|
||||||
|
foreach (EnvironmentVariableTarget target in targets)
|
||||||
|
{
|
||||||
|
Logger.Verbose(Component, string.Concat("Searching ", target.ToString(), " scope for environment variable '", Name, "'."));
|
||||||
|
|
||||||
|
string value;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = Environment.GetEnvironmentVariable(Name, target);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Logger.Verbose(Component, string.Concat("Failed to read ", target.ToString(), " scope for environment variable '", Name, "': ", exception.Message));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
Logger.Information(Component, string.Concat("Found environment variable '", Name, "' in ", target.ToString(), " scope."));
|
||||||
|
WriteObject(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string scopeDescription = Scope.HasValue ? string.Concat(Scope.Value.ToString(), " scope") : "Process, User, or Machine scope";
|
||||||
|
Logger.Information(Component, string.Concat("Environment variable '", Name, "' was not found in ", scopeDescription, "."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalFolder[] folders = client.List(connection, ProjectId, Environment, Path);
|
InfisicalFolder[] folders = client.List(connection, ProjectId, Environment, Path);
|
||||||
|
Logger.Information("Get-InfisicalFolder", string.Concat("Returned ", folders.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " folder(s) from '", Path ?? "/", "'."));
|
||||||
foreach (InfisicalFolder folder in folders)
|
foreach (InfisicalFolder folder in folders)
|
||||||
{
|
{
|
||||||
WriteObject(folder);
|
WriteObject(folder);
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.Organizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Get, "InfisicalOrganization", DefaultParameterSetName = "List")]
|
||||||
|
[OutputType(typeof(InfisicalOrganization))]
|
||||||
|
public sealed class GetInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||||
|
|
||||||
|
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
InfisicalOrganization organization = client.Retrieve(connection, OrganizationId);
|
||||||
|
if (organization != null)
|
||||||
|
{
|
||||||
|
WriteObject(organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalOrganization[] organizations = client.List(connection);
|
||||||
|
Logger.Information("Get-InfisicalOrganization", string.Concat("Returned ", organizations.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " organization(s)."));
|
||||||
|
foreach (InfisicalOrganization organization in organizations)
|
||||||
|
{
|
||||||
|
WriteObject(organization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("GetInfisicalOrganizationCmdlet", "GetOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalPkiSubscriber[] all = client.ListPkiSubscribers(connection, ProjectId);
|
InfisicalPkiSubscriber[] all = client.ListPkiSubscribers(connection, ProjectId);
|
||||||
|
Logger.Information("Get-InfisicalPkiSubscriber", string.Concat("Returned ", all.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " PKI subscriber(s)."));
|
||||||
foreach (InfisicalPkiSubscriber subscriber in all)
|
foreach (InfisicalPkiSubscriber subscriber in all)
|
||||||
{
|
{
|
||||||
WriteObject(subscriber);
|
WriteObject(subscriber);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalProject[] projects = client.List(connection, Type, IncludeRoles.IsPresent);
|
InfisicalProject[] projects = client.List(connection, Type, IncludeRoles.IsPresent);
|
||||||
|
Logger.Information("Get-InfisicalProject", string.Concat("Returned ", projects.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " project(s)."));
|
||||||
foreach (InfisicalProject project in projects)
|
foreach (InfisicalProject project in projects)
|
||||||
{
|
{
|
||||||
WriteObject(project);
|
WriteObject(project);
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Get, "InfisicalSANList")]
|
||||||
|
[OutputType(typeof(string[]))]
|
||||||
|
public sealed class GetInfisicalSANListCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
private const string Component = "GetInfisicalSANListCmdlet";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[ValidateNotNullOrEmpty]
|
||||||
|
public string InclusionExpression { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[ValidateNotNullOrEmpty]
|
||||||
|
public string ExclusionExpression { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Regex includeRegex = !string.IsNullOrEmpty(InclusionExpression) ? new Regex(InclusionExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
|
||||||
|
Regex excludeRegex = !string.IsNullOrEmpty(ExclusionExpression) ? new Regex(ExclusionExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
|
||||||
|
|
||||||
|
List<string> sans = new List<string>();
|
||||||
|
HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
string deviceName = Dns.GetHostName();
|
||||||
|
AddUnique(sans, seen, deviceName);
|
||||||
|
|
||||||
|
HashSet<string> suffixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
string globalDomain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
|
||||||
|
if (!string.IsNullOrEmpty(globalDomain)) { suffixes.Add(globalDomain); }
|
||||||
|
|
||||||
|
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
|
||||||
|
foreach (NetworkInterface adapter in adapters)
|
||||||
|
{
|
||||||
|
if (adapter.OperationalStatus != OperationalStatus.Up) { continue; }
|
||||||
|
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) { continue; }
|
||||||
|
|
||||||
|
IPInterfaceProperties props = adapter.GetIPProperties();
|
||||||
|
if (!string.IsNullOrEmpty(props.DnsSuffix)) { suffixes.Add(props.DnsSuffix); }
|
||||||
|
|
||||||
|
foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
|
||||||
|
{
|
||||||
|
IPAddress ip = unicast.Address;
|
||||||
|
if (ip.AddressFamily == AddressFamily.InterNetwork && IsRfc1918OrCgnat(ip))
|
||||||
|
{
|
||||||
|
AddUnique(sans, seen, ip.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string suffix in suffixes)
|
||||||
|
{
|
||||||
|
string trimmed = suffix.Trim().TrimStart('.');
|
||||||
|
if (!string.IsNullOrEmpty(trimmed))
|
||||||
|
{
|
||||||
|
AddUnique(sans, seen, string.Concat(deviceName, ".", trimmed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddUnique(sans, seen, "127.0.0.1");
|
||||||
|
AddUnique(sans, seen, "::1");
|
||||||
|
|
||||||
|
List<string> filtered = new List<string>(sans.Count);
|
||||||
|
foreach (string san in sans)
|
||||||
|
{
|
||||||
|
if (includeRegex != null && !includeRegex.IsMatch(san)) { continue; }
|
||||||
|
if (excludeRegex != null && excludeRegex.IsMatch(san)) { continue; }
|
||||||
|
filtered.Add(san);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteObject(filtered.ToArray(), false);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException(Component, "GetSANList", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRfc1918OrCgnat(IPAddress ip)
|
||||||
|
{
|
||||||
|
byte[] bytes = ip.GetAddressBytes();
|
||||||
|
if (bytes.Length != 4) { return false; }
|
||||||
|
|
||||||
|
if (bytes[0] == 10) { return true; }
|
||||||
|
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) { return true; }
|
||||||
|
if (bytes[0] == 192 && bytes[1] == 168) { return true; }
|
||||||
|
if (bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127) { return true; }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddUnique(List<string> list, HashSet<string> seen, string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value)) { return; }
|
||||||
|
if (seen.Add(value)) { list.Add(value); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
using PSInfisicalAPI.Connections;
|
using PSInfisicalAPI.Connections;
|
||||||
using PSInfisicalAPI.Models;
|
using PSInfisicalAPI.Models;
|
||||||
@@ -57,8 +58,13 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
InfisicalSecret secret = client.Retrieve(connection, query);
|
InfisicalSecret secret = client.Retrieve(connection, query);
|
||||||
if (secret != null)
|
if (secret != null)
|
||||||
{
|
{
|
||||||
|
Logger.Information("Get-InfisicalSecret", string.Concat("Returned 1 secret for '", SecretName, "'."));
|
||||||
WriteObject(secret);
|
WriteObject(secret);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Information("Get-InfisicalSecret", string.Concat("No secret returned for '", SecretName, "'."));
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -79,6 +85,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
};
|
};
|
||||||
|
|
||||||
InfisicalSecret[] secrets = client.List(connection, listQuery);
|
InfisicalSecret[] secrets = client.List(connection, listQuery);
|
||||||
|
Logger.Information("Get-InfisicalSecret", string.Concat("Returned ", secrets.Length.ToString(CultureInfo.InvariantCulture), " secret(s) from '", SecretPath ?? "/", "' (recursive=", Recursive.IsPresent ? "true" : "false", ", includeImports=", IncludeImports.IsPresent ? "true" : "false", ")."));
|
||||||
foreach (InfisicalSecret secret in secrets)
|
foreach (InfisicalSecret secret in secrets)
|
||||||
{
|
{
|
||||||
WriteObject(secret);
|
WriteObject(secret);
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.SubOrganizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Get, "InfisicalSubOrganization", DefaultParameterSetName = "List")]
|
||||||
|
[OutputType(typeof(InfisicalSubOrganization))]
|
||||||
|
public sealed class GetInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string SubOrganizationId { get; set; }
|
||||||
|
|
||||||
|
[Parameter(ParameterSetName = "List")] public int? Limit { get; set; }
|
||||||
|
[Parameter(ParameterSetName = "List")] public int? Offset { get; set; }
|
||||||
|
[Parameter(ParameterSetName = "List")] public string Search { get; set; }
|
||||||
|
[Parameter(ParameterSetName = "List")] public string OrderBy { get; set; }
|
||||||
|
|
||||||
|
[Parameter(ParameterSetName = "List")]
|
||||||
|
[ValidateSet("asc", "desc")]
|
||||||
|
public string OrderDirection { get; set; }
|
||||||
|
|
||||||
|
[Parameter(ParameterSetName = "List")] public SwitchParameter IsAccessible { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||||
|
|
||||||
|
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
InfisicalSubOrganization subOrganization = client.Retrieve(connection, SubOrganizationId);
|
||||||
|
if (subOrganization != null)
|
||||||
|
{
|
||||||
|
WriteObject(subOrganization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool? isAccessible = MyInvocation.BoundParameters.ContainsKey("IsAccessible") ? (bool?)IsAccessible.IsPresent : null;
|
||||||
|
InfisicalSubOrganization[] subOrganizations = client.List(connection, Limit, Offset, Search, OrderBy, OrderDirection, isAccessible);
|
||||||
|
Logger.Information("Get-InfisicalSubOrganization", string.Concat("Returned ", subOrganizations.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " sub-organization(s)."));
|
||||||
|
foreach (InfisicalSubOrganization subOrganization in subOrganizations)
|
||||||
|
{
|
||||||
|
WriteObject(subOrganization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("GetInfisicalSubOrganizationCmdlet", "GetSubOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalTag[] tags = client.List(connection, ProjectId);
|
InfisicalTag[] tags = client.List(connection, ProjectId);
|
||||||
|
Logger.Information("Get-InfisicalTag", string.Concat("Returned ", tags.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " tag(s)."));
|
||||||
foreach (InfisicalTag tag in tags)
|
foreach (InfisicalTag tag in tags)
|
||||||
{
|
{
|
||||||
WriteObject(tag);
|
WriteObject(tag);
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Security;
|
||||||
|
using PSInfisicalAPI.Common;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Imports;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsData.Import, "InfisicalSecret")]
|
||||||
|
[OutputType(typeof(Dictionary<string, SecureString>))]
|
||||||
|
[OutputType(typeof(Dictionary<string, string>))]
|
||||||
|
public sealed class ImportInfisicalSecretCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, Position = 0)]
|
||||||
|
[ValidateNotNull]
|
||||||
|
public FileInfo Path { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true)]
|
||||||
|
public InfisicalImportFormat Format { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public InfisicalDuplicateKeyBehavior DuplicateKeyBehavior { get; set; } = InfisicalDuplicateKeyBehavior.Error;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public SwitchParameter AsPlainText { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Alias("Prefix")]
|
||||||
|
public string SecretsPrefix { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Alias("ForcePrefix")]
|
||||||
|
public SwitchParameter ForceSecretsPrefix { get; set; }
|
||||||
|
|
||||||
|
protected override void EndProcessing()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Path.Refresh();
|
||||||
|
if (!Path.Exists)
|
||||||
|
{
|
||||||
|
throw new InfisicalImportException(string.Concat("Import path does not exist: ", Path.FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
IInfisicalImporter importer = InfisicalImporterFactory.Create(Format);
|
||||||
|
IList<KeyValuePair<string, string>> pairs = importer.Import(Path);
|
||||||
|
Logger.Information("Import-InfisicalSecret", string.Concat("Parsed ", pairs.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " secret pair(s) from '", Path.FullName, "' (format=", Format.ToString(), ")."));
|
||||||
|
|
||||||
|
if (AsPlainText.IsPresent)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> plain = BuildDictionary<string>(pairs, value => value ?? string.Empty);
|
||||||
|
Logger.Information("Import-InfisicalSecret", string.Concat("Built plain-text dictionary with ", plain.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " entry/entries."));
|
||||||
|
WriteObject(plain);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dictionary<string, SecureString> secure = BuildDictionary<SecureString>(pairs, value => SecureStringUtility.ToReadOnlySecureString(value ?? string.Empty));
|
||||||
|
Logger.Information("Import-InfisicalSecret", string.Concat("Built SecureString dictionary with ", secure.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " entry/entries."));
|
||||||
|
WriteObject(secure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("ImportInfisicalSecretCmdlet", "ImportSecret", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, TValue> BuildDictionary<TValue>(
|
||||||
|
IList<KeyValuePair<string, string>> pairs,
|
||||||
|
Func<string, TValue> valueSelector)
|
||||||
|
{
|
||||||
|
Dictionary<string, TValue> dictionary = new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, string> pair in pairs)
|
||||||
|
{
|
||||||
|
if (pair.Key == null) { continue; }
|
||||||
|
string key = InfisicalPrefix.Apply(pair.Key, SecretsPrefix, ForceSecretsPrefix.IsPresent);
|
||||||
|
|
||||||
|
if (dictionary.ContainsKey(key))
|
||||||
|
{
|
||||||
|
if (DuplicateKeyBehavior == InfisicalDuplicateKeyBehavior.Error)
|
||||||
|
{
|
||||||
|
throw new InfisicalConfigurationException(string.Concat("Duplicate secret name encountered: ", key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DuplicateKeyBehavior == InfisicalDuplicateKeyBehavior.LastWins)
|
||||||
|
{
|
||||||
|
dictionary[key] = valueSelector(pair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary[key] = valueSelector(pair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.Organizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.New, "InfisicalOrganization", SupportsShouldProcess = true)]
|
||||||
|
[OutputType(typeof(InfisicalOrganization))]
|
||||||
|
public sealed class NewInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
|
||||||
|
[Parameter] public string Slug { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ShouldProcess(Name, "Create Infisical organization"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||||
|
InfisicalOrganization organization = client.Create(connection, Name, Slug);
|
||||||
|
if (organization != null)
|
||||||
|
{
|
||||||
|
WriteObject(organization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("NewInfisicalOrganizationCmdlet", "CreateOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,10 +58,12 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
Secrets = InfisicalBulkSecretConverter.ToCreateItems(Secrets)
|
Secrets = InfisicalBulkSecretConverter.ToCreateItems(Secrets)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Logger.Information("New-InfisicalSecret", string.Concat("Bulk-creating ", Secrets.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " secret(s)."));
|
||||||
InfisicalSecretsClient bulkClient = new InfisicalSecretsClient(HttpClient, Logger);
|
InfisicalSecretsClient bulkClient = new InfisicalSecretsClient(HttpClient, Logger);
|
||||||
InfisicalSecret[] created = bulkClient.CreateBatch(connection, bulk);
|
InfisicalSecret[] created = bulkClient.CreateBatch(connection, bulk);
|
||||||
if (created != null)
|
if (created != null)
|
||||||
{
|
{
|
||||||
|
Logger.Information("New-InfisicalSecret", string.Concat("Server returned ", created.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " created secret(s)."));
|
||||||
foreach (InfisicalSecret secret in created) { WriteObject(secret); }
|
foreach (InfisicalSecret secret in created) { WriteObject(secret); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.SubOrganizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.New, "InfisicalSubOrganization", SupportsShouldProcess = true)]
|
||||||
|
[OutputType(typeof(InfisicalSubOrganization))]
|
||||||
|
public sealed class NewInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
|
||||||
|
[Parameter(Mandatory = true, Position = 1)] public string Slug { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ShouldProcess(Name, "Create Infisical sub-organization"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||||
|
InfisicalSubOrganization subOrganization = client.Create(connection, Name, Slug);
|
||||||
|
if (subOrganization != null)
|
||||||
|
{
|
||||||
|
WriteObject(subOrganization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("NewInfisicalSubOrganizationCmdlet", "CreateSubOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Organizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Remove, "InfisicalOrganization", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||||
|
public sealed class RemoveInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public SwitchParameter PassThru { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
|
||||||
|
if (!ShouldProcess(OrganizationId, "Remove Infisical organization"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||||
|
client.Delete(connection, OrganizationId);
|
||||||
|
|
||||||
|
if (PassThru.IsPresent)
|
||||||
|
{
|
||||||
|
WriteObject(OrganizationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("RemoveInfisicalOrganizationCmdlet", "DeleteOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
SecretNames = SecretNames
|
SecretNames = SecretNames
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Logger.Information("Remove-InfisicalSecret", string.Concat("Bulk-removing ", SecretNames.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " secret(s)."));
|
||||||
client.DeleteBatch(connection, bulk);
|
client.DeleteBatch(connection, bulk);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
if (PassThru.IsPresent)
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.SubOrganizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Remove, "InfisicalSubOrganization", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||||
|
public sealed class RemoveInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string SubOrganizationId { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public SwitchParameter PassThru { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
|
||||||
|
if (!ShouldProcess(SubOrganizationId, "Remove Infisical sub-organization"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||||
|
client.Delete(connection, SubOrganizationId);
|
||||||
|
|
||||||
|
if (PassThru.IsPresent)
|
||||||
|
{
|
||||||
|
WriteObject(SubOrganizationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("RemoveInfisicalSubOrganizationCmdlet", "DeleteSubOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -89,18 +89,23 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
public SwitchParameter ContinueOnError { get; set; }
|
public SwitchParameter ContinueOnError { get; set; }
|
||||||
|
|
||||||
[Parameter(ValueFromPipeline = true)]
|
[Parameter(ValueFromPipeline = true)]
|
||||||
[Alias("Secrets", "InputObject")]
|
[Alias("Secret", "InputObject")]
|
||||||
public InfisicalSecret[] Secret { get; set; }
|
public InfisicalSecret[] Secrets { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Prefix { get; set; }
|
[Alias("Prefix")]
|
||||||
|
public string SecretsPrefix { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Alias("ForcePrefix")]
|
||||||
|
public SwitchParameter ForceSecretsPrefix { get; set; }
|
||||||
|
|
||||||
private readonly List<InfisicalSecret> _secretBuffer = new List<InfisicalSecret>();
|
private readonly List<InfisicalSecret> _secretBuffer = new List<InfisicalSecret>();
|
||||||
|
|
||||||
protected override void ProcessRecord()
|
protected override void ProcessRecord()
|
||||||
{
|
{
|
||||||
if (Secret == null) { return; }
|
if (Secrets == null) { return; }
|
||||||
foreach (InfisicalSecret secret in Secret)
|
foreach (InfisicalSecret secret in Secrets)
|
||||||
{
|
{
|
||||||
if (secret != null) { _secretBuffer.Add(secret); }
|
if (secret != null) { _secretBuffer.Add(secret); }
|
||||||
}
|
}
|
||||||
@@ -116,6 +121,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
|
|
||||||
if (!ShouldProcess(target, "Start process with Infisical secrets")) { return; }
|
if (!ShouldProcess(target, "Start process with Infisical secrets")) { return; }
|
||||||
|
|
||||||
|
int envVarCount = EnvironmentVariables != null ? EnvironmentVariables.Count : 0;
|
||||||
|
Logger.Information("Start-InfisicalProcess", string.Concat("Injecting ", _secretBuffer.Count.ToString(System.Globalization.CultureInfo.InvariantCulture), " secret(s) and ", envVarCount.ToString(System.Globalization.CultureInfo.InvariantCulture), " explicit environment variable(s) into process environment."));
|
||||||
|
|
||||||
InfisicalProcessOptions options = new InfisicalProcessOptions
|
InfisicalProcessOptions options = new InfisicalProcessOptions
|
||||||
{
|
{
|
||||||
FilePath = FilePath,
|
FilePath = FilePath,
|
||||||
@@ -135,7 +143,8 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
LogOutput = LogOutput.IsPresent,
|
LogOutput = LogOutput.IsPresent,
|
||||||
ContinueOnError = ContinueOnError.IsPresent,
|
ContinueOnError = ContinueOnError.IsPresent,
|
||||||
Secrets = _secretBuffer.ToArray(),
|
Secrets = _secretBuffer.ToArray(),
|
||||||
Prefix = Prefix
|
SecretsPrefix = SecretsPrefix,
|
||||||
|
ForceSecretsPrefix = ForceSecretsPrefix.IsPresent
|
||||||
};
|
};
|
||||||
|
|
||||||
InfisicalProcessResult result = InfisicalProcessRunner.Run(options, Logger);
|
InfisicalProcessResult result = InfisicalProcessRunner.Run(options, Logger);
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.Organizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsData.Update, "InfisicalOrganization", SupportsShouldProcess = true)]
|
||||||
|
[OutputType(typeof(InfisicalOrganization))]
|
||||||
|
public sealed class UpdateInfisicalOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public string Name { get; set; }
|
||||||
|
[Parameter] public string Slug { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
|
||||||
|
if (!ShouldProcess(OrganizationId, "Update Infisical organization"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
|
||||||
|
InfisicalOrganization organization = client.Update(connection, OrganizationId, Name, Slug);
|
||||||
|
if (organization != null)
|
||||||
|
{
|
||||||
|
WriteObject(organization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("UpdateInfisicalOrganizationCmdlet", "UpdateOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,10 +56,12 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
Secrets = InfisicalBulkSecretConverter.ToUpdateItems(Secrets)
|
Secrets = InfisicalBulkSecretConverter.ToUpdateItems(Secrets)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Logger.Information("Update-InfisicalSecret", string.Concat("Bulk-updating ", Secrets.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " secret(s)."));
|
||||||
InfisicalSecretsClient bulkClient = new InfisicalSecretsClient(HttpClient, Logger);
|
InfisicalSecretsClient bulkClient = new InfisicalSecretsClient(HttpClient, Logger);
|
||||||
InfisicalSecret[] updated = bulkClient.UpdateBatch(connection, bulk);
|
InfisicalSecret[] updated = bulkClient.UpdateBatch(connection, bulk);
|
||||||
if (updated != null)
|
if (updated != null)
|
||||||
{
|
{
|
||||||
|
Logger.Information("Update-InfisicalSecret", string.Concat("Server returned ", updated.Length.ToString(System.Globalization.CultureInfo.InvariantCulture), " updated secret(s)."));
|
||||||
foreach (InfisicalSecret secret in updated) { WriteObject(secret); }
|
foreach (InfisicalSecret secret in updated) { WriteObject(secret); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.SubOrganizations;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsData.Update, "InfisicalSubOrganization", SupportsShouldProcess = true)]
|
||||||
|
[OutputType(typeof(InfisicalSubOrganization))]
|
||||||
|
public sealed class UpdateInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string SubOrganizationId { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public string Name { get; set; }
|
||||||
|
[Parameter] public string Slug { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
|
||||||
|
if (!ShouldProcess(SubOrganizationId, "Update Infisical sub-organization"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
|
||||||
|
InfisicalSubOrganization subOrganization = client.Update(connection, SubOrganizationId, Name, Slug);
|
||||||
|
if (subOrganization != null)
|
||||||
|
{
|
||||||
|
WriteObject(subOrganization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("UpdateInfisicalSubOrganizationCmdlet", "UpdateSubOrganization", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Common
|
||||||
|
{
|
||||||
|
public static class InfisicalPrefix
|
||||||
|
{
|
||||||
|
public static string Apply(string original, string prefix, bool force)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(prefix)) { return original ?? string.Empty; }
|
||||||
|
if (original == null) { return prefix; }
|
||||||
|
if (!force && original.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return original; }
|
||||||
|
return string.Concat(prefix, original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,18 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
public const string UpdateTag = "UpdateTag";
|
public const string UpdateTag = "UpdateTag";
|
||||||
public const string DeleteTag = "DeleteTag";
|
public const string DeleteTag = "DeleteTag";
|
||||||
|
|
||||||
|
public const string ListOrganizations = "ListOrganizations";
|
||||||
|
public const string RetrieveOrganization = "RetrieveOrganization";
|
||||||
|
public const string CreateOrganization = "CreateOrganization";
|
||||||
|
public const string UpdateOrganization = "UpdateOrganization";
|
||||||
|
public const string DeleteOrganization = "DeleteOrganization";
|
||||||
|
|
||||||
|
public const string ListSubOrganizations = "ListSubOrganizations";
|
||||||
|
public const string RetrieveSubOrganization = "RetrieveSubOrganization";
|
||||||
|
public const string CreateSubOrganization = "CreateSubOrganization";
|
||||||
|
public const string UpdateSubOrganization = "UpdateSubOrganization";
|
||||||
|
public const string DeleteSubOrganization = "DeleteSubOrganization";
|
||||||
|
|
||||||
public const string ListInternalCertificateAuthorities = "ListInternalCertificateAuthorities";
|
public const string ListInternalCertificateAuthorities = "ListInternalCertificateAuthorities";
|
||||||
public const string RetrieveInternalCertificateAuthority = "RetrieveInternalCertificateAuthority";
|
public const string RetrieveInternalCertificateAuthority = "RetrieveInternalCertificateAuthority";
|
||||||
public const string SearchCertificates = "SearchCertificates";
|
public const string SearchCertificates = "SearchCertificates";
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
RegisterEnvironments(Candidates);
|
RegisterEnvironments(Candidates);
|
||||||
RegisterFolders(Candidates);
|
RegisterFolders(Candidates);
|
||||||
RegisterTags(Candidates);
|
RegisterTags(Candidates);
|
||||||
|
RegisterOrganizations(Candidates);
|
||||||
|
RegisterSubOrganizations(Candidates);
|
||||||
RegisterPki(Candidates);
|
RegisterPki(Candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +498,112 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RegisterOrganizations(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||||
|
{
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.ListOrganizations,
|
||||||
|
Resource = "Organizations",
|
||||||
|
Version = "v2",
|
||||||
|
Method = "GET",
|
||||||
|
Template = "/api/v2/organizations",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.RetrieveOrganization,
|
||||||
|
Resource = "Organizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "GET",
|
||||||
|
Template = "/api/v1/organization/{organizationId}",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.CreateOrganization,
|
||||||
|
Resource = "Organizations",
|
||||||
|
Version = "v2",
|
||||||
|
Method = "POST",
|
||||||
|
Template = "/api/v2/organizations",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.UpdateOrganization,
|
||||||
|
Resource = "Organizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "PATCH",
|
||||||
|
Template = "/api/v1/organization/{organizationId}",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.DeleteOrganization,
|
||||||
|
Resource = "Organizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "DELETE",
|
||||||
|
Template = "/api/v1/organization/{organizationId}",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterSubOrganizations(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||||
|
{
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.ListSubOrganizations,
|
||||||
|
Resource = "SubOrganizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "GET",
|
||||||
|
Template = "/api/v1/sub-organizations",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.RetrieveSubOrganization,
|
||||||
|
Resource = "SubOrganizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "GET",
|
||||||
|
Template = "/api/v1/sub-organizations/{subOrgId}",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.CreateSubOrganization,
|
||||||
|
Resource = "SubOrganizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "POST",
|
||||||
|
Template = "/api/v1/sub-organizations",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.UpdateSubOrganization,
|
||||||
|
Resource = "SubOrganizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "PATCH",
|
||||||
|
Template = "/api/v1/sub-organizations/{subOrgId}",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
{
|
||||||
|
Name = InfisicalEndpointNames.DeleteSubOrganization,
|
||||||
|
Resource = "SubOrganizations",
|
||||||
|
Version = "v1",
|
||||||
|
Method = "DELETE",
|
||||||
|
Template = "/api/v1/sub-organizations/{subOrgId}",
|
||||||
|
RequiresAuthorization = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static void RegisterPki(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
private static void RegisterPki(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||||
{
|
{
|
||||||
Add(map, new InfisicalEndpointDefinition
|
Add(map, new InfisicalEndpointDefinition
|
||||||
|
|||||||
@@ -75,6 +75,13 @@ namespace PSInfisicalAPI.Errors
|
|||||||
public InfisicalExportException(string message, Exception innerException) : base(message, innerException) { }
|
public InfisicalExportException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class InfisicalImportException : InfisicalException
|
||||||
|
{
|
||||||
|
public InfisicalImportException() { }
|
||||||
|
public InfisicalImportException(string message) : base(message) { }
|
||||||
|
public InfisicalImportException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
|
|
||||||
public class InfisicalConfigurationException : InfisicalException
|
public class InfisicalConfigurationException : InfisicalException
|
||||||
{
|
{
|
||||||
public InfisicalConfigurationException() { }
|
public InfisicalConfigurationException() { }
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Imports
|
||||||
|
{
|
||||||
|
public sealed class EnvInfisicalImporter : IInfisicalImporter
|
||||||
|
{
|
||||||
|
public IList<KeyValuePair<string, string>> Import(FileInfo path)
|
||||||
|
{
|
||||||
|
if (path == null) { throw new InfisicalImportException("Path is required for ENV import."); }
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
|
||||||
|
string[] lines = File.ReadAllLines(path.FullName);
|
||||||
|
|
||||||
|
foreach (string raw in lines)
|
||||||
|
{
|
||||||
|
if (raw == null) { continue; }
|
||||||
|
string line = raw.Trim();
|
||||||
|
if (line.Length == 0) { continue; }
|
||||||
|
if (line[0] == '#') { continue; }
|
||||||
|
|
||||||
|
int idx = line.IndexOf('=');
|
||||||
|
if (idx <= 0) { continue; }
|
||||||
|
|
||||||
|
string key = line.Substring(0, idx).Trim();
|
||||||
|
string value = line.Substring(idx + 1);
|
||||||
|
if (key.Length == 0) { continue; }
|
||||||
|
|
||||||
|
result.Add(new KeyValuePair<string, string>(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Imports
|
||||||
|
{
|
||||||
|
public interface IInfisicalImporter
|
||||||
|
{
|
||||||
|
IList<KeyValuePair<string, string>> Import(FileInfo path);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Imports
|
||||||
|
{
|
||||||
|
public static class InfisicalImporterFactory
|
||||||
|
{
|
||||||
|
public static IInfisicalImporter Create(InfisicalImportFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case InfisicalImportFormat.Json: return new JsonInfisicalImporter();
|
||||||
|
case InfisicalImportFormat.Yaml: return new YamlInfisicalImporter();
|
||||||
|
case InfisicalImportFormat.Env: return new EnvInfisicalImporter();
|
||||||
|
case InfisicalImportFormat.Xml: return new XmlInfisicalImporter();
|
||||||
|
default: throw new InfisicalImportException(string.Concat("Unsupported import format: ", format.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Imports
|
||||||
|
{
|
||||||
|
public sealed class JsonInfisicalImporter : IInfisicalImporter
|
||||||
|
{
|
||||||
|
public IList<KeyValuePair<string, string>> Import(FileInfo path)
|
||||||
|
{
|
||||||
|
if (path == null) { throw new InfisicalImportException("Path is required for JSON import."); }
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
|
||||||
|
string text = File.ReadAllText(path.FullName);
|
||||||
|
JToken root = JToken.Parse(text);
|
||||||
|
|
||||||
|
if (root.Type == JTokenType.Array)
|
||||||
|
{
|
||||||
|
foreach (JToken item in (JArray)root)
|
||||||
|
{
|
||||||
|
if (item == null || item.Type != JTokenType.Object) { continue; }
|
||||||
|
string key = ReadString((JObject)item, "SecretName") ?? ReadString((JObject)item, "secretName");
|
||||||
|
string value = ReadString((JObject)item, "SecretValue") ?? ReadString((JObject)item, "secretValue");
|
||||||
|
if (string.IsNullOrEmpty(key)) { continue; }
|
||||||
|
result.Add(new KeyValuePair<string, string>(key, value ?? string.Empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (root.Type == JTokenType.Object)
|
||||||
|
{
|
||||||
|
foreach (JProperty prop in ((JObject)root).Properties())
|
||||||
|
{
|
||||||
|
if (prop == null || string.IsNullOrEmpty(prop.Name)) { continue; }
|
||||||
|
string value = prop.Value != null && prop.Value.Type != JTokenType.Null ? prop.Value.ToString() : string.Empty;
|
||||||
|
result.Add(new KeyValuePair<string, string>(prop.Name, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InfisicalImportException("JSON import expects an array of secret objects or a flat key/value object.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReadString(JObject obj, string name)
|
||||||
|
{
|
||||||
|
JToken token;
|
||||||
|
if (!obj.TryGetValue(name, out token) || token == null || token.Type == JTokenType.Null) { return null; }
|
||||||
|
return token.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Imports
|
||||||
|
{
|
||||||
|
public sealed class XmlInfisicalImporter : IInfisicalImporter
|
||||||
|
{
|
||||||
|
public IList<KeyValuePair<string, string>> Import(FileInfo path)
|
||||||
|
{
|
||||||
|
if (path == null) { throw new InfisicalImportException("Path is required for XML import."); }
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
|
||||||
|
XmlDocument document = new XmlDocument();
|
||||||
|
document.Load(path.FullName);
|
||||||
|
|
||||||
|
XmlNode root = document.DocumentElement;
|
||||||
|
if (root == null || !string.Equals(root.LocalName, "Secrets", System.StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
throw new InfisicalImportException("XML import expects a root <Secrets> element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (XmlNode node in root.ChildNodes)
|
||||||
|
{
|
||||||
|
if (node == null || node.NodeType != XmlNodeType.Element) { continue; }
|
||||||
|
if (!string.Equals(node.LocalName, "Secret", System.StringComparison.Ordinal)) { continue; }
|
||||||
|
|
||||||
|
string key = ReadChild(node, "SecretName");
|
||||||
|
string value = ReadChild(node, "SecretValue");
|
||||||
|
if (string.IsNullOrEmpty(key)) { continue; }
|
||||||
|
result.Add(new KeyValuePair<string, string>(key, value ?? string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReadChild(XmlNode parent, string name)
|
||||||
|
{
|
||||||
|
foreach (XmlNode child in parent.ChildNodes)
|
||||||
|
{
|
||||||
|
if (child == null || child.NodeType != XmlNodeType.Element) { continue; }
|
||||||
|
if (string.Equals(child.LocalName, name, System.StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return child.InnerText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Imports
|
||||||
|
{
|
||||||
|
public sealed class YamlInfisicalImporter : IInfisicalImporter
|
||||||
|
{
|
||||||
|
public IList<KeyValuePair<string, string>> Import(FileInfo path)
|
||||||
|
{
|
||||||
|
if (path == null) { throw new InfisicalImportException("Path is required for YAML import."); }
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
|
||||||
|
string text = File.ReadAllText(path.FullName);
|
||||||
|
|
||||||
|
IDeserializer deserializer = new DeserializerBuilder().Build();
|
||||||
|
object root = deserializer.Deserialize<object>(text);
|
||||||
|
if (root == null) { return result; }
|
||||||
|
|
||||||
|
IDictionary rootMap = root as IDictionary;
|
||||||
|
if (rootMap != null && rootMap.Contains("Secrets"))
|
||||||
|
{
|
||||||
|
IList entries = rootMap["Secrets"] as IList;
|
||||||
|
if (entries != null)
|
||||||
|
{
|
||||||
|
foreach (object entry in entries)
|
||||||
|
{
|
||||||
|
IDictionary map = entry as IDictionary;
|
||||||
|
if (map == null) { continue; }
|
||||||
|
string key = AsString(map["SecretName"]);
|
||||||
|
string value = AsString(map["SecretValue"]);
|
||||||
|
if (string.IsNullOrEmpty(key)) { continue; }
|
||||||
|
result.Add(new KeyValuePair<string, string>(key, value ?? string.Empty));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootMap != null)
|
||||||
|
{
|
||||||
|
foreach (DictionaryEntry kvp in rootMap)
|
||||||
|
{
|
||||||
|
string key = AsString(kvp.Key);
|
||||||
|
if (string.IsNullOrEmpty(key)) { continue; }
|
||||||
|
result.Add(new KeyValuePair<string, string>(key, AsString(kvp.Value) ?? string.Empty));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InfisicalImportException("YAML import expects a 'Secrets' root list or a flat key/value mapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string AsString(object value)
|
||||||
|
{
|
||||||
|
if (value == null) { return null; }
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PSInfisicalAPI.Models
|
||||||
|
{
|
||||||
|
public enum InfisicalImportFormat
|
||||||
|
{
|
||||||
|
Json,
|
||||||
|
Yaml,
|
||||||
|
Env,
|
||||||
|
Xml
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Models
|
||||||
|
{
|
||||||
|
public sealed class InfisicalOrganization
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Slug { get; set; }
|
||||||
|
public string CustomerId { get; set; }
|
||||||
|
public bool AuthEnforced { get; set; }
|
||||||
|
public bool ScimEnabled { get; set; }
|
||||||
|
public DateTimeOffset? CreatedAtUtc { get; set; }
|
||||||
|
public DateTimeOffset? UpdatedAtUtc { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Models
|
||||||
|
{
|
||||||
|
public sealed class InfisicalSubOrganization
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Slug { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
public bool IsAccessible { get; set; }
|
||||||
|
public DateTimeOffset? CreatedAtUtc { get; set; }
|
||||||
|
public DateTimeOffset? UpdatedAtUtc { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.Serialization;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Organizations
|
||||||
|
{
|
||||||
|
public sealed class InfisicalOrganizationClient
|
||||||
|
{
|
||||||
|
private const string Component = "OrganizationClient";
|
||||||
|
|
||||||
|
private readonly IInfisicalLogger _logger;
|
||||||
|
private readonly JsonInfisicalSerializer _serializer;
|
||||||
|
private readonly InfisicalApiInvoker _invoker;
|
||||||
|
|
||||||
|
public InfisicalOrganizationClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
|
||||||
|
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||||
|
_serializer = new JsonInfisicalSerializer();
|
||||||
|
_invoker = new InfisicalApiInvoker(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalOrganization[] List(InfisicalConnection connection)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, "Attempting to list Infisical organizations. Please Wait...");
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListOrganizations, "ListOrganizations", null, null, null);
|
||||||
|
InfisicalOrganizationListResponseDto dto = _serializer.Deserialize<InfisicalOrganizationListResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalOrganization[] mapped = InfisicalOrganizationMapper.MapMany(dto != null ? dto.Organizations : null);
|
||||||
|
_logger.Information(Component, "Infisical organization list retrieval was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical organization list retrieval failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalOrganization Retrieve(InfisicalConnection connection, string organizationId)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical organization '", organizationId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveOrganization, "RetrieveOrganization", pathParameters, null, null);
|
||||||
|
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
|
||||||
|
_logger.Information(Component, "Infisical organization retrieval was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical organization retrieval failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalOrganization Create(InfisicalConnection connection, string name, string slug)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
|
||||||
|
|
||||||
|
InfisicalOrganizationCreateRequestDto request = new InfisicalOrganizationCreateRequestDto { Name = name, Slug = slug };
|
||||||
|
string body = _serializer.Serialize(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to create Infisical organization '", name, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateOrganization, "CreateOrganization", null, null, body);
|
||||||
|
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
|
||||||
|
_logger.Information(Component, "Infisical organization creation was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical organization creation failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalOrganization Update(InfisicalConnection connection, string organizationId, string name, string slug)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
|
||||||
|
InfisicalOrganizationUpdateRequestDto request = new InfisicalOrganizationUpdateRequestDto { Name = name, Slug = slug };
|
||||||
|
string body = _serializer.Serialize(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to update Infisical organization '", organizationId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateOrganization, "UpdateOrganization", pathParameters, null, body);
|
||||||
|
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
|
||||||
|
_logger.Information(Component, "Infisical organization update was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical organization update failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(InfisicalConnection connection, string organizationId)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to delete Infisical organization '", organizationId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteOrganization, "DeleteOrganization", pathParameters, null, null);
|
||||||
|
response.Clear();
|
||||||
|
_logger.Information(Component, "Infisical organization deletion was successful.");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical organization deletion failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Organizations
|
||||||
|
{
|
||||||
|
internal sealed class InfisicalOrganizationResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("id")] public string Id { get; set; }
|
||||||
|
[JsonProperty("_id")] public string InternalId { get; set; }
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("slug")] public string Slug { get; set; }
|
||||||
|
[JsonProperty("customerId")] public string CustomerId { get; set; }
|
||||||
|
[JsonProperty("authEnforced")] public bool AuthEnforced { get; set; }
|
||||||
|
[JsonProperty("scimEnabled")] public bool ScimEnabled { get; set; }
|
||||||
|
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
|
||||||
|
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalOrganizationListResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("organizations")] public List<InfisicalOrganizationResponseDto> Organizations { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalOrganizationSingleResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("organization")] public InfisicalOrganizationResponseDto Organization { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalOrganizationCreateRequestDto
|
||||||
|
{
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalOrganizationUpdateRequestDto
|
||||||
|
{
|
||||||
|
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; }
|
||||||
|
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Organizations
|
||||||
|
{
|
||||||
|
internal static class InfisicalOrganizationMapper
|
||||||
|
{
|
||||||
|
public static InfisicalOrganization Map(InfisicalOrganizationResponseDto dto)
|
||||||
|
{
|
||||||
|
if (dto == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InfisicalOrganization
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
|
||||||
|
Name = dto.Name,
|
||||||
|
Slug = dto.Slug,
|
||||||
|
CustomerId = dto.CustomerId,
|
||||||
|
AuthEnforced = dto.AuthEnforced,
|
||||||
|
ScimEnabled = dto.ScimEnabled,
|
||||||
|
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
|
||||||
|
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InfisicalOrganization[] MapMany(IEnumerable<InfisicalOrganizationResponseDto> items)
|
||||||
|
{
|
||||||
|
if (items == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<InfisicalOrganization>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InfisicalOrganization> results = new List<InfisicalOrganization>();
|
||||||
|
foreach (InfisicalOrganizationResponseDto dto in items)
|
||||||
|
{
|
||||||
|
InfisicalOrganization mapped = Map(dto);
|
||||||
|
if (mapped != null)
|
||||||
|
{
|
||||||
|
results.Add(mapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeOffset? ParseTimestamp(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeOffset parsed;
|
||||||
|
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
|
||||||
|
{
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ namespace PSInfisicalAPI.Process
|
|||||||
public bool LogOutput { get; set; }
|
public bool LogOutput { get; set; }
|
||||||
public bool ContinueOnError { get; set; }
|
public bool ContinueOnError { get; set; }
|
||||||
public InfisicalSecret[] Secrets { get; set; }
|
public InfisicalSecret[] Secrets { get; set; }
|
||||||
public string Prefix { get; set; }
|
public string SecretsPrefix { get; set; }
|
||||||
|
public bool ForceSecretsPrefix { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using PSInfisicalAPI.Common;
|
||||||
using PSInfisicalAPI.Logging;
|
using PSInfisicalAPI.Logging;
|
||||||
using PSInfisicalAPI.Models;
|
using PSInfisicalAPI.Models;
|
||||||
using PSInfisicalAPI.Security;
|
using PSInfisicalAPI.Security;
|
||||||
@@ -23,7 +24,7 @@ namespace PSInfisicalAPI.Process
|
|||||||
|
|
||||||
if (options.EnvironmentVariables != null && options.EnvironmentVariables.Count > 0)
|
if (options.EnvironmentVariables != null && options.EnvironmentVariables.Count > 0)
|
||||||
{
|
{
|
||||||
Log(logger, string.Concat("Injecting ", options.EnvironmentVariables.Count, " explicit environment variable(s) into the process."));
|
LogInformation(logger, string.Concat("Injecting ", options.EnvironmentVariables.Count, " explicit environment variable(s) into the process."));
|
||||||
foreach (DictionaryEntry entry in options.EnvironmentVariables)
|
foreach (DictionaryEntry entry in options.EnvironmentVariables)
|
||||||
{
|
{
|
||||||
if (entry.Key == null) { continue; }
|
if (entry.Key == null) { continue; }
|
||||||
@@ -35,11 +36,11 @@ namespace PSInfisicalAPI.Process
|
|||||||
|
|
||||||
if (options.Secrets == null || options.Secrets.Length == 0) { return; }
|
if (options.Secrets == null || options.Secrets.Length == 0) { return; }
|
||||||
|
|
||||||
Log(logger, string.Concat("Injecting ", options.Secrets.Length, " Infisical secret(s) into the process environment."));
|
LogInformation(logger, string.Concat("Injecting ", options.Secrets.Length, " Infisical secret(s) into the process environment."));
|
||||||
foreach (InfisicalSecret secret in options.Secrets)
|
foreach (InfisicalSecret secret in options.Secrets)
|
||||||
{
|
{
|
||||||
if (secret == null || string.IsNullOrEmpty(secret.SecretName) || secret.SecretValue == null) { continue; }
|
if (secret == null || string.IsNullOrEmpty(secret.SecretName) || secret.SecretValue == null) { continue; }
|
||||||
string name = string.IsNullOrEmpty(options.Prefix) ? secret.SecretName : string.Concat(options.Prefix, secret.SecretName);
|
string name = InfisicalPrefix.Apply(secret.SecretName, options.SecretsPrefix, options.ForceSecretsPrefix);
|
||||||
SecureStringUtility.UsePlainText(secret.SecretValue, plain =>
|
SecureStringUtility.UsePlainText(secret.SecretValue, plain =>
|
||||||
{
|
{
|
||||||
processEnv[name] = plain;
|
processEnv[name] = plain;
|
||||||
@@ -192,5 +193,10 @@ namespace PSInfisicalAPI.Process
|
|||||||
{
|
{
|
||||||
if (logger != null) { logger.Verbose(Component, message); }
|
if (logger != null) { logger.Verbose(Component, message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void LogInformation(IInfisicalLogger logger, string message)
|
||||||
|
{
|
||||||
|
if (logger != null) { logger.Information(Component, message); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ namespace PSInfisicalAPI.Secrets
|
|||||||
InfisicalSecretListResponseDto dto = _serializer.Deserialize<InfisicalSecretListResponseDto>(response.Body);
|
InfisicalSecretListResponseDto dto = _serializer.Deserialize<InfisicalSecretListResponseDto>(response.Body);
|
||||||
response.Clear();
|
response.Clear();
|
||||||
|
|
||||||
InfisicalSecret[] mapped = InfisicalSecretMapper.MapMany(dto != null ? dto.Secrets : null);
|
InfisicalSecret[] mapped = MergeListAndImports(dto);
|
||||||
_logger.Information(Component, "Infisical secrets retrieval was successful.");
|
_logger.Information(Component, string.Concat("Infisical secrets retrieval was successful. Returned ", mapped.Length.ToString(CultureInfo.InvariantCulture), " secret(s)."));
|
||||||
return mapped;
|
return mapped;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
@@ -465,6 +465,66 @@ namespace PSInfisicalAPI.Secrets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InfisicalSecret[] MergeListAndImports(InfisicalSecretListResponseDto dto)
|
||||||
|
{
|
||||||
|
if (dto == null) { return Array.Empty<InfisicalSecret>(); }
|
||||||
|
|
||||||
|
InfisicalSecret[] local = InfisicalSecretMapper.MapMany(dto.Secrets);
|
||||||
|
|
||||||
|
if (dto.Imports == null || dto.Imports.Count == 0)
|
||||||
|
{
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, InfisicalSecret> merged = new Dictionary<string, InfisicalSecret>(StringComparer.Ordinal);
|
||||||
|
int importsTotal = 0;
|
||||||
|
|
||||||
|
foreach (InfisicalSecretImportDto import in dto.Imports)
|
||||||
|
{
|
||||||
|
if (import == null) { continue; }
|
||||||
|
InfisicalSecret[] importedSecrets = InfisicalSecretMapper.MapMany(import.Secrets);
|
||||||
|
importsTotal += importedSecrets.Length;
|
||||||
|
|
||||||
|
_logger.Information(Component, string.Concat(
|
||||||
|
"Including ",
|
||||||
|
importedSecrets.Length.ToString(CultureInfo.InvariantCulture),
|
||||||
|
" secret(s) from import '",
|
||||||
|
import.SecretPath ?? string.Empty,
|
||||||
|
"' (environment='",
|
||||||
|
import.Environment ?? string.Empty,
|
||||||
|
"')."));
|
||||||
|
|
||||||
|
foreach (InfisicalSecret secret in importedSecrets)
|
||||||
|
{
|
||||||
|
if (secret == null || string.IsNullOrEmpty(secret.SecretName)) { continue; }
|
||||||
|
merged[secret.SecretName] = secret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int overrides = 0;
|
||||||
|
foreach (InfisicalSecret secret in local)
|
||||||
|
{
|
||||||
|
if (secret == null || string.IsNullOrEmpty(secret.SecretName)) { continue; }
|
||||||
|
if (merged.ContainsKey(secret.SecretName)) { overrides++; }
|
||||||
|
merged[secret.SecretName] = secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Information(Component, string.Concat(
|
||||||
|
"Merged secrets: local=",
|
||||||
|
local.Length.ToString(CultureInfo.InvariantCulture),
|
||||||
|
", imports=",
|
||||||
|
importsTotal.ToString(CultureInfo.InvariantCulture),
|
||||||
|
", local-overrode-import=",
|
||||||
|
overrides.ToString(CultureInfo.InvariantCulture),
|
||||||
|
", final=",
|
||||||
|
merged.Count.ToString(CultureInfo.InvariantCulture),
|
||||||
|
"."));
|
||||||
|
|
||||||
|
InfisicalSecret[] result = new InfisicalSecret[merged.Count];
|
||||||
|
merged.Values.CopyTo(result, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private InfisicalHttpResponse SendWithVersionFallback(
|
private InfisicalHttpResponse SendWithVersionFallback(
|
||||||
InfisicalConnection connection,
|
InfisicalConnection connection,
|
||||||
string endpointName,
|
string endpointName,
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using PSInfisicalAPI.Serialization;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.SubOrganizations
|
||||||
|
{
|
||||||
|
public sealed class InfisicalSubOrganizationClient
|
||||||
|
{
|
||||||
|
private const string Component = "SubOrganizationClient";
|
||||||
|
|
||||||
|
private readonly IInfisicalLogger _logger;
|
||||||
|
private readonly JsonInfisicalSerializer _serializer;
|
||||||
|
private readonly InfisicalApiInvoker _invoker;
|
||||||
|
|
||||||
|
public InfisicalSubOrganizationClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
|
||||||
|
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||||
|
_serializer = new JsonInfisicalSerializer();
|
||||||
|
_invoker = new InfisicalApiInvoker(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalSubOrganization[] List(InfisicalConnection connection, int? limit, int? offset, string search, string orderBy, string orderDirection, bool? isAccessible)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string>> queryParameters = new List<KeyValuePair<string, string>>();
|
||||||
|
if (limit.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("limit", limit.Value.ToString(CultureInfo.InvariantCulture))); }
|
||||||
|
if (offset.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("offset", offset.Value.ToString(CultureInfo.InvariantCulture))); }
|
||||||
|
if (!string.IsNullOrEmpty(search)) { queryParameters.Add(new KeyValuePair<string, string>("search", search)); }
|
||||||
|
if (!string.IsNullOrEmpty(orderBy)) { queryParameters.Add(new KeyValuePair<string, string>("orderBy", orderBy)); }
|
||||||
|
if (!string.IsNullOrEmpty(orderDirection)) { queryParameters.Add(new KeyValuePair<string, string>("orderDirection", orderDirection)); }
|
||||||
|
if (isAccessible.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("isAccessible", isAccessible.Value ? "true" : "false")); }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, "Attempting to list Infisical sub-organizations. Please Wait...");
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListSubOrganizations, "ListSubOrganizations", null, queryParameters, null);
|
||||||
|
InfisicalSubOrganizationListResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationListResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalSubOrganization[] mapped = InfisicalSubOrganizationMapper.MapMany(dto != null ? dto.SubOrganizations : null);
|
||||||
|
_logger.Information(Component, "Infisical sub-organization list retrieval was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical sub-organization list retrieval failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalSubOrganization Retrieve(InfisicalConnection connection, string subOrganizationId)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveSubOrganization, "RetrieveSubOrganization", pathParameters, null, null);
|
||||||
|
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
|
||||||
|
_logger.Information(Component, "Infisical sub-organization retrieval was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical sub-organization retrieval failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalSubOrganization Create(InfisicalConnection connection, string name, string slug)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
|
||||||
|
if (string.IsNullOrEmpty(slug)) { throw new InfisicalConfigurationException("Slug is required."); }
|
||||||
|
|
||||||
|
InfisicalSubOrganizationCreateRequestDto request = new InfisicalSubOrganizationCreateRequestDto { Name = name, Slug = slug };
|
||||||
|
string body = _serializer.Serialize(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to create Infisical sub-organization '", name, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateSubOrganization, "CreateSubOrganization", null, null, body);
|
||||||
|
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
|
||||||
|
_logger.Information(Component, "Infisical sub-organization creation was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical sub-organization creation failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalSubOrganization Update(InfisicalConnection connection, string subOrganizationId, string name, string slug)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
|
||||||
|
InfisicalSubOrganizationUpdateRequestDto request = new InfisicalSubOrganizationUpdateRequestDto { Name = name, Slug = slug };
|
||||||
|
string body = _serializer.Serialize(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to update Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateSubOrganization, "UpdateSubOrganization", pathParameters, null, body);
|
||||||
|
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
|
||||||
|
_logger.Information(Component, "Infisical sub-organization update was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical sub-organization update failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(InfisicalConnection connection, string subOrganizationId)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to delete Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteSubOrganization, "DeleteSubOrganization", pathParameters, null, null);
|
||||||
|
response.Clear();
|
||||||
|
_logger.Information(Component, "Infisical sub-organization deletion was successful.");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical sub-organization deletion failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.SubOrganizations
|
||||||
|
{
|
||||||
|
internal sealed class InfisicalSubOrganizationResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("id")] public string Id { get; set; }
|
||||||
|
[JsonProperty("_id")] public string InternalId { get; set; }
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("slug")] public string Slug { get; set; }
|
||||||
|
[JsonProperty("organizationId")] public string OrganizationId { get; set; }
|
||||||
|
[JsonProperty("orgId")] public string OrgId { get; set; }
|
||||||
|
[JsonProperty("isAccessible")] public bool IsAccessible { get; set; }
|
||||||
|
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
|
||||||
|
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalSubOrganizationListResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("subOrganizations")] public List<InfisicalSubOrganizationResponseDto> SubOrganizations { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalSubOrganizationSingleResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("subOrganization")] public InfisicalSubOrganizationResponseDto SubOrganization { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalSubOrganizationCreateRequestDto
|
||||||
|
{
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("slug")] public string Slug { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalSubOrganizationUpdateRequestDto
|
||||||
|
{
|
||||||
|
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; }
|
||||||
|
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.SubOrganizations
|
||||||
|
{
|
||||||
|
internal static class InfisicalSubOrganizationMapper
|
||||||
|
{
|
||||||
|
public static InfisicalSubOrganization Map(InfisicalSubOrganizationResponseDto dto)
|
||||||
|
{
|
||||||
|
if (dto == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InfisicalSubOrganization
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
|
||||||
|
Name = dto.Name,
|
||||||
|
Slug = dto.Slug,
|
||||||
|
OrganizationId = !string.IsNullOrEmpty(dto.OrganizationId) ? dto.OrganizationId : dto.OrgId,
|
||||||
|
IsAccessible = dto.IsAccessible,
|
||||||
|
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
|
||||||
|
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InfisicalSubOrganization[] MapMany(IEnumerable<InfisicalSubOrganizationResponseDto> items)
|
||||||
|
{
|
||||||
|
if (items == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<InfisicalSubOrganization>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InfisicalSubOrganization> results = new List<InfisicalSubOrganization>();
|
||||||
|
foreach (InfisicalSubOrganizationResponseDto dto in items)
|
||||||
|
{
|
||||||
|
InfisicalSubOrganization mapped = Map(dto);
|
||||||
|
if (mapped != null)
|
||||||
|
{
|
||||||
|
results.Add(mapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeOffset? ParseTimestamp(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeOffset parsed;
|
||||||
|
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
|
||||||
|
{
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user