BitwardenPS.psm1
#Region './Private/ArgumentCompleters/VaultCollectionCompleter.ps1' 0 $VaultCollectionCompleter = { param ( $CommandName, $ParamName, $Collection, $CommandAst, $fakeBoundParameters ) if (!$script:VaultCollections) { Get-BwVaultCollections | Out-Null } $Collection = $Collection -replace "'", '' ($script:VaultCollections).name | Where-Object { $_ -match "$Collection" } | ForEach-Object { "'$_'" } } Register-ArgumentCompleter -CommandName Get-BwVaultItem -ParameterName Collection -ScriptBlock $VaultCollectionCompleter #EndRegion './Private/ArgumentCompleters/VaultCollectionCompleter.ps1' 18 #Region './Private/ArgumentCompleters/VaultOrgCompleter.ps1' 0 $VaultOrgCompleter = { param ( $CommandName, $ParamName, $Organization, $CommandAst, $fakeBoundParameters ) if (!$script:VaultCollections) { Get-BwVaultOrgs | Out-Null } $Organization = $Organization -replace "'", '' ($script:VaultOrgs).name | Where-Object { $_ -match "$Organization" } | ForEach-Object { "'$_'" } } Register-ArgumentCompleter -CommandName Get-BwVaultItem -ParameterName Organization -ScriptBlock $VaultOrgCompleter #EndRegion './Private/ArgumentCompleters/VaultOrgCompleter.ps1' 18 #Region './Private/Invoke/Invoke-BwsCli.ps1' 0 function Invoke-BwsCli { <# .SYNOPSIS Runs Bitwarden Secrets Cli commands .DESCRIPTION Calls bws command #> [cmdletbinding()] Param( $Arguments ) $bw = Get-Command bws if (!$bw) { Write-Error 'Bitwarden Secrets CLI is not installed' return $false } $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = $bw.Path $pinfo.RedirectStandardError = $false $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $Arguments $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() $stdout = $p.StandardOutput.ReadToEnd() $stdout | ConvertFrom-Json } #EndRegion './Private/Invoke/Invoke-BwsCli.ps1' 34 #Region './Private/Invoke/Invoke-PublicApi.ps1' 0 function Invoke-PublicApi { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $Endpoint, [ValidateSet('Get', 'Put', 'Post', 'Delete')] $Method = 'Get', $Body = '' ) if (!$script:BwPublicApi) { Write-Error 'Not connected to Bitwarden Public API' return $false } $Uri = 'https://{0}/{1}' -f $script:BwPublicApi.Server, $Endpoint $Headers = @{ Authorization = ('Bearer {0}' -f $script:BwPublicApi.Token) 'Content-Type' = 'application/json' } if ($Method -eq 'Post') { if ($Body -ne '') { $RestMethod.Body = $Body } } $RestMethod = @{ Uri = $Uri Method = $Method Headers = $Headers } Write-Verbose ($RestMethod | ConvertTo-Json) $Response = Invoke-RestMethod @RestMethod -SkipHttpErrorCheck if ($Response.data) { $Response.data } else { $Response } } #EndRegion './Private/Invoke/Invoke-PublicApi.ps1' 43 #Region './Private/Invoke/Invoke-VaultApi.ps1' 0 function Invoke-VaultApi { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $Endpoint, [ValidateSet('Get', 'Put', 'Post', 'Delete')] $Method = 'Get', $QueryParams = '', $Body = '', $ContentType = 'application/json', $OutFile = '' ) Start-BwRestServer | Out-Null #Write-Verbose ($script:BwRestServer | ConvertTo-Json) $Uri = 'http://{0}:{1}/{2}' -f $script:BwRestServer.Hostname, $script:BwRestServer.Port, $Endpoint if ($QueryParams -ne '') { $UriBuilder = [System.UriBuilder]$Uri $UriBuilder.Query = $QueryParams.ToString() $Uri = $UriBuilder.Uri.OriginalString } Write-Verbose "$($Method.ToUpper()) [ $Uri ]" $Headers = @{ 'Accept' = 'application/json' } $RestMethod = @{ Uri = $Uri Method = $Method } if ($Method -eq 'Post' -or $Method -eq 'Put') { $headers.'Content-Type' = $ContentType if ($Body -ne '') { $RestMethod.Body = $Body } $RestMethod.Headers = $Headers } if ($OutFile) { Invoke-WebRequest @RestMethod -OutFile $OutFile } else { Invoke-RestMethod @RestMethod -SkipHttpErrorCheck } } #EndRegion './Private/Invoke/Invoke-VaultApi.ps1' 49 #Region './Private/Invoke/Invoke-VaultCli.ps1' 0 function Invoke-VaultCli { <# .SYNOPSIS Runs Bitwarden Cli commands .DESCRIPTION Calls bw command #> [cmdletbinding()] Param( $Arguments ) $bw = Get-Command bw if (!$bw) { Write-Error 'Bitwarden CLI is not installed' return $false } $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = $bw.Path $pinfo.RedirectStandardError = $false $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $Arguments $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() $stdout = $p.StandardOutput.ReadToEnd() $stdout | ConvertFrom-Json } #EndRegion './Private/Invoke/Invoke-VaultCli.ps1' 34 #Region './Public/Public API/Collections/Get-BwPublicCollections.ps1' 0 function Get-BwPublicCollections { [CmdletBinding()] Param( $Id = '' ) $Endpoint = 'public/collections' if ($Id -ne '') { $Endpoint = '{0}/{1}' -f $Endpoint, $Id } Invoke-PublicApi -Endpoint $Endpoint } #EndRegion './Public/Public API/Collections/Get-BwPublicCollections.ps1' 13 #Region './Public/Public API/Connect/Connect-BwPublicApi.ps1' 0 function Connect-BwPublicApi { <# .SYNOPSIS Connects to Bitwarden Public API .DESCRIPTION Generates OAuth token to interact with public API .PARAMETER Credentials Client ID and Secret in PSCredential object .PARAMETER Server Bitwarden identity server hostname, default identity.bitwarden.com #> [CmdletBinding()] Param( [pscredential]$Credentials = (Get-Credential -Message 'Enter ClientID and Secret'), $Server = 'identity.bitwarden.com' ) if ($Server -eq 'identity.bitwarden.com') { $ApiServer = 'api.bitwarden.com' $Uri = 'https://{0}/connect/token' -f $Server } else { $ApiServer = $Server $Uri = 'https://{0}/identity/connect/token' -f $Server } $AuthRequest = @{ Uri = $Uri ContentType = 'application/x-www-form-urlencoded' Body = @{ grant_type = 'client_credentials' scope = 'api.organization' client_id = $Credentials.GetNetworkCredential().UserName client_secret = $Credentials.GetNetworkCredential().Password } } $Response = Invoke-RestMethod @AuthRequest -Method Post -SkipHttpErrorCheck if ($Response) { Write-Verbose 'Connected to the Bitwarden Public API' $script:BwPublicApi = @{ Token = $Response.access_token Server = $ApiServer } $true } else { Write-Error $Response $false } } #EndRegion './Public/Public API/Connect/Connect-BwPublicApi.ps1' 55 #Region './Public/Public API/Events/Get-BwPublicEvents.ps1' 0 function Get-BwPublicEvents { <# .SYNOPSIS Gets Bitwarden events .DESCRIPTION Calls the /public/events endpoint to retrieve audit trail #> [CmdletBinding()] Param() $EventMsg = [PSCustomObject]@{ '1000' = 'Logged In' '1001' = 'Changed account password' '1002' = 'Enabled/updated two-step login' '1003' = 'Disabled two-step login' '1004' = 'Recovered account from two-step login' '1005' = 'Login attempted failed with incorrect password' '1006' = 'Login attempt failed with incorrect two-step login' '1007' = 'User exported their individual vault items' '1008' = 'User updated a password issued through Admin Password Reset' '1009' = 'User migrated their decryption key with Key Connector' '1100' = 'Created item item-identifier' '1101' = 'Edited item item-identifier' '1102' = 'Permanently Deleted item item-identifier' '1103' = 'Created attachment for item item-identifier' '1104' = 'Deleted attachment for item item-identifier' '1105' = 'Shared item item-identifier' '1106' = 'Edited collections for item item-identifier' '1107' = 'Viewed item item-identifier' '1108' = 'Viewed password for item item-identifier' '1109' = 'Viewed hidden field for item item-identifier' '1110' = 'Viewed security code for item item-identifier' '1111' = 'Copied password for item item-identifier' '1112' = 'Copied hidden field for item item-identifier' '1113' = 'Copied security code for item item-identifier' '1114' = 'Auto-filled item item-identifier' '1115' = 'Sent item item-identifier to trash' '1116' = 'Restored item item-identifier' '1117' = 'Viewed Card Number for item item-identifier' '1300' = 'Created collection collection-identifier' '1301' = 'Edited collection collection-identifier' '1302' = 'Deleted collection collection-identifier' '1400' = 'Created group group-identifier' '1401' = 'Edited group group-identifier' '1402' = 'Deleted group group-identifier' '1500' = 'Invited user user-identifier' '1501' = 'Confirmed user user-identifier' '1502' = 'Edited user user-identifier' '1503' = 'Removed user user-identifier' '1504' = 'Edited groups for user user-identifier' '1505' = 'Unlinked SSO' '1506' = 'user-identifier enrolled in Master Password Reset' '1507' = 'user-identifier withdrew from Master Password Reset' '1508' = 'Master Password was reset for user-identifier' '1509' = 'Reset SSO link for user user-identifier' '1510' = 'user-identifer logged in using SSO for the first time' '1511' = 'Revoked organization access for user-identifier' '1512' = 'Restores organization access for user-identifier' '1600' = 'Edited organization settings' '1601' = 'Purged organization vault' '1603' = 'Organization Vault access by a managing Provider' '1604' = 'Organization enabled SSO' '1605' = 'Organization disabled SSO' '1606' = 'Organization enabled Key Connector' '1607' = 'Organization disabled Key Connector' '1608' = 'Families Sponsorships synced' '1700' = 'Updated a Policy' } $Endpoint = 'public/events' $Events = Invoke-PublicApi -Endpoint $Endpoint $Members = Get-PublicMembers $Groups = Get-PublicGroups foreach ($Event in $Events) { if ($Event.groupId) { $GroupName = ($Groups | Where-Object { $_.id -eq $Event.groupId }).name } if ($Event.memberId) { $MemberEmail = ($Members | Where-Object { $_.userId -eq $Event.memberId }).email } $ActingUser = ($Members | Where-Object { $_.userId -eq $Event.actingUserId }).email $TypeId = $Event.type $Message = $EventMsg.$TypeId -replace 'item-identifier', $Event.itemId -replace 'collection-identifier', $Event.collectionId -replace 'group-identifier', $GroupName -replace 'member-identifier', $MemberEmail -replace 'user-identifier', $ActingUser [PSCustomObject]@{ message = $Message type = $Event.type itemId = $Event.itemId collectionId = $Event.collectionId groupId = $Event.groupId policyId = $Event.policyId memberId = $Event.memberId actingUserId = $Event.actingUserId actingUser = $ActingUser date = $Event.date ipAddress = $Event.ipAddress } } } #EndRegion './Public/Public API/Events/Get-BwPublicEvents.ps1' 99 #Region './Public/Public API/Groups/Get-BwPublicGroupMemberIds.ps1' 0 function Get-BwPublicGroupMemberIds { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Id Parameter description .EXAMPLE An example .NOTES General notes #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $Id ) $Endpoint = 'public/groups/{0}/member-ids' -f $Id Invoke-PublicApi -Endpoint $Endpoint } #EndRegion './Public/Public API/Groups/Get-BwPublicGroupMemberIds.ps1' 27 #Region './Public/Public API/Groups/Get-BwPublicGroups.ps1' 0 function Get-BwPublicGroups { [CmdletBinding()] Param( $Id = '' ) $Endpoint = 'public/groups' if ($Id -ne '') { $Endpoint = '{0}/{1}' -f $Endpoint, $Id } Invoke-PublicApi -Endpoint $Endpoint } #EndRegion './Public/Public API/Groups/Get-BwPublicGroups.ps1' 12 #Region './Public/Public API/Members/Get-BwPublicMemberGroupIds.ps1' 0 function Get-BwPublicMemberGroupIds { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $Id ) $Endpoint = 'public/members/{0}/group-ids' -f $Id Invoke-PublicApi -Endpoint $Endpoint } #EndRegion './Public/Public API/Members/Get-BwPublicMemberGroupIds.ps1' 11 #Region './Public/Public API/Members/Get-BwPublicMembers.ps1' 0 function Get-BwPublicMembers { [CmdletBinding()] Param( $Id = '' ) $Endpoint = 'public/members' if ($Id -ne '') { $Endpoint = '{0}/{1}' -f $Endpoint, $Id } Invoke-PublicApi -Endpoint $Endpoint } #EndRegion './Public/Public API/Members/Get-BwPublicMembers.ps1' 12 #Region './Public/Public API/Members/Send-BwPublicMemberInvite.ps1' 0 function Send-BwPublicMemberInvite { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $Id ) $Endpoint = 'public/members/{0}/reinvite' -f $id Invoke-PublicApi -Endpoint $Endpoint -Method Post } #EndRegion './Public/Public API/Members/Send-BwPublicMemberInvite.ps1' 11 #Region './Public/Secrets/Get-BwSecret.ps1' 0 function Get-BwSecret { [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(Mandatory = $true, ParameterSetName = 'Id')] $Id ) $Arguments = switch ($PSCmdlet.ParameterSetName) { 'List' { @( 'list' 'secrets' ) } 'Id' { 'get' 'secret' $Id } } Invoke-BwsCli -Arguments $Arguments } #EndRegion './Public/Secrets/Get-BwSecret.ps1' 23 #Region './Public/Secrets/Set-BwSecretsAccessToken.ps1' 0 function Set-BwSecretsAccessToken { [CmdletBinding(SupportsShouldProcess)] Param( $AccessToken, [switch]$Unset ) # Set session variable for cli commands if ($PSCmdlet.ShouldProcess('BWS_ACCESS_TOKEN')) { if ($Unset.IsPresent) { [Environment]::SetEnvironmentVariable('BWS_ACCESS_TOKEN', '') } else { [Environment]::SetEnvironmentVariable('BWS_ACCESS_TOKEN', $AccessToken) } } } #EndRegion './Public/Secrets/Set-BwSecretsAccessToken.ps1' 17 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultAttachment.ps1' 0 function Get-BwVaultAttachment { <# .SYNOPSIS Gets Bitwarden Vault Attachments .DESCRIPTION Calls Get /object/attachment/{id} to download attachments .PARAMETER Id Attachment id .PARAMETER ItemId Item Guid .PARAMETER FilePath Path to save file .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $Id, [Parameter(Mandatory = $true)] $ItemId, $FilePath = '' ) $Endpoint = 'object/attachment/{0}' -f $Id $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('itemid', $ItemId) | Out-Null $VaultApi = @{ Endpoint = $Endpoint QueryParams = $QueryParams } if ($FilePath) { $VaultApi.OutFile = $FilePath } Invoke-VaultApi @VaultApi } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultAttachment.ps1' 46 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultItemNotes.ps1' 0 function Get-BwVaultItemNotes { <# .SYNOPSIS Returns Notes of a vault item .DESCRIPTION Calls /object/notes/{id} endpoint to retrieve notes .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Request = Invoke-VaultApi -Endpoint ('object/notes/{0}' -f $Id) if ($Request.success) { $Request.data.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultItemNotes.ps1' 31 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultItemPassword.ps1' 0 function Get-BwVaultItemPassword { <# .SYNOPSIS Returns password of a vault item .DESCRIPTION Calls /object/password/{id} endpoint to retrieve password .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Request = Invoke-VaultApi -Endpoint ('object/password/{0}' -f $Id) if ($Request.success) { $Request.data.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultItemPassword.ps1' 31 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultItemPwExposed.ps1' 0 function Get-BwVaultItemPwExposed { <# .SYNOPSIS Returns count of times a vault item password has been exposed .DESCRIPTION Calls /object/exposed/{id} endpoint to retrieve count .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Request = Invoke-VaultApi -Endpoint ('object/exposed/{0}' -f $Id) if ($Request.success) { $Request.data.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultItemPwExposed.ps1' 32 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultItemTotp.ps1' 0 function Get-BwVaultItemTotp { <# .SYNOPSIS Returns TOTP of a vault item .DESCRIPTION Calls /object/totp/{id} endpoint to retrieve TOTP .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Request = Invoke-VaultApi -Endpoint ('object/totp/{0}' -f $Id) if ($Request.success) { $Request.data.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultItemTotp.ps1' 31 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultItemUri.ps1' 0 function Get-BwVaultItemUri { <# .SYNOPSIS Returns URI of a vault item .DESCRIPTION Calls /object/uri/{id} endpoint to retrieve URI .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Request = Invoke-VaultApi -Endpoint ('object/uri/{0}' -f $Id) if ($Request.success) { $Request.data.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultItemUri.ps1' 32 #Region './Public/Vault API/Attachments & Fields/Get-BwVaultItemUsername.ps1' 0 function Get-BwVaultItemUsername { <# .SYNOPSIS Returns username of a vault item .DESCRIPTION Calls /object/username/{id} endpoint to retrieve username .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Request = Invoke-VaultApi -Endpoint ('object/username/{0}' -f $Id) if ($Request.success) { $Request.data.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Attachments & Fields/Get-BwVaultItemUsername.ps1' 32 #Region './Public/Vault API/Attachments & Fields/New-BwVaultAttachment.ps1' 0 function New-BwVaultAttachment { <# .SYNOPSIS Creates attachment in vault .DESCRIPTION POSTS multipart form data to /attachment .PARAMETER ItemId Item guid .PARAMETER Content String contents of file .PARAMETER FileName Name of file to create .PARAMETER Path Path to file instead of string contents #> [CmdletBinding(DefaultParameterSetName = 'Content', SupportsShouldProcess)] Param ( [parameter(Mandatory = $true)] $ItemId, [parameter(ParameterSetName = 'Content', Mandatory = $True)] [string]$Content, [parameter(ParameterSetName = 'Content', Mandatory = $True)] [string]$FileName, [validatescript({ Test-Path -PathType Leaf -Path $_ })] [parameter(ParameterSetName = 'File', Mandatory = $True)] [string]$Path ) Process { $MultipartContent = [System.Net.Http.MultipartFormDataContent]::new() $FileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') $FileHeader.Name = 'file' if ($Content) { $ByteArray = [System.Text.Encoding]::UTF8.GetBytes($Content) $FileStream = New-Object -TypeName 'System.IO.MemoryStream' -ArgumentList (, $ByteArray) } else { $FileName = Split-Path -Path $Path -Leaf $FileStream = [System.IO.FileStream]::new($Path, [System.IO.FileMode]::Open) } $FileContent = [System.Net.Http.StreamContent]::new($FileStream) $FileHeader.FileName = $FileName $FileContent.Headers.ContentDisposition = $fileHeader $FileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('multipart/form-data') $MultipartContent.Add($FileContent) $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('itemid', $ItemId) | Out-Null if ($PSCmdlet.ShouldProcess($FileName)) { try { $response = Invoke-VaultApi -Endpoint 'attachment' -QueryParams $QueryParams -Method Post -Body $MultipartContent -ContentType 'multipart/form-data' } catch { Write-Error "Upload attachment failed: $_" throw $_ } finally { $fileStream.Close() } } $response } } #EndRegion './Public/Vault API/Attachments & Fields/New-BwVaultAttachment.ps1' 71 #Region './Public/Vault API/Attachments & Fields/Remove-BwVaultAttachment.ps1' 0 function Remove-BwVaultAttachment { <# .SYNOPSIS Deletes Bitwarden Vault Attachments .DESCRIPTION Calls DELETE /object/item/{id} to move items to the trash .PARAMETER Id Attachment id .PARAMETER ItemId Item Guid .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(Mandatory = $true)] $Id, [Parameter(Mandatory = $true)] $ItemId ) $Endpoint = 'object/attachment/{0}' -f $Id $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('itemid', $ItemId) | Out-Null $VaultApi = @{ Method = 'Delete' Endpoint = $Endpoint Body = $Body QueryParams = $QueryParams } if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi @VaultApi } } #EndRegion './Public/Vault API/Attachments & Fields/Remove-BwVaultAttachment.ps1' 42 #Region './Public/Vault API/Collections & Organizations/Confirm-BwVaultOrgMember.ps1' 0 function Confirm-BwVaultOrgMember { <# .SYNOPSIS Gets Bitwarden Vault Org Collections .DESCRIPTION Calls /list/object/org-members .PARAMETER Id Guid of Org Member .PARAMETER OrganizationId Guid of Organization .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id, [Parameter(Mandatory = $true)] $OrganizationId ) Process { $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('organizationid', $OrganizationId) | Out-Null $Endpoint = 'confirm/org-member/{0}' -f $Id if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi -Endpoint $Endpoint -QueryParams $QueryParams } } } #EndRegion './Public/Vault API/Collections & Organizations/Confirm-BwVaultOrgMember.ps1' 37 #Region './Public/Vault API/Collections & Organizations/Get-BwVaultCollections.ps1' 0 function Get-BwVaultCollections { <# .SYNOPSIS Gets Bitwarden Vault Collections .DESCRIPTION Calls /list/object/collections .PARAMETER Search Organization name to search for .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding()] Param( $Search = '' ) $Endpoint = 'list/object/collections' $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) if ($Search -ne '') { $QueryParams.Add('search', $Search) | Out-Null } $Request = Invoke-VaultApi -Endpoint $Endpoint -QueryParams $QueryParams if ($Request.success) { $Request.data.data $script:VaultCollections = $Request.data.data } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Collections & Organizations/Get-BwVaultCollections.ps1' 37 #Region './Public/Vault API/Collections & Organizations/Get-BwVaultOrgCollection.ps1' 0 function Get-BwVaultOrgCollection { <# .SYNOPSIS Gets Bitwarden Vault Org Collections .DESCRIPTION Calls /list/object/org-collections or /object/org-collection/{id} .PARAMETER Id Guid of Collection .PARAMETER OrganizationId Guid of Organization .PARAMETER Search Search parameters .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ParameterSetName = 'Single', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'Single', Mandatory = $true)] [Parameter(ParameterSetName = 'List', Mandatory = $true)] $OrganizationId, [Parameter(ParameterSetName = 'List')] $Search = '' ) $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('organizationid', $OrganizationId) | Out-Null switch ($PSCmdlet.ParameterSetName) { 'List' { $Endpoint = 'list/object/org-collections' if ($Search -ne '') { $QueryParams.Add('search', $Search) | Out-Null } } 'Single' { $Endpoint = 'object/org-collection/{0}' -f $Id } } $Request = Invoke-VaultApi -Endpoint $Endpoint -QueryParams $QueryParams if ($Request.success) { if ($Request.data.data) { $Request.data.data } elseif ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Collections & Organizations/Get-BwVaultOrgCollection.ps1' 64 #Region './Public/Vault API/Collections & Organizations/Get-BwVaultOrgMembers.ps1' 0 function Get-BwVaultOrgMembers { <# .SYNOPSIS Gets Bitwarden Vault Org Collections .DESCRIPTION Calls /list/object/org-members .PARAMETER OrganizationId Guid of Organization .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] $OrganizationId ) $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('organizationid', $OrganizationId) | Out-Null $Endpoint = 'list/object/org-members' $Request = Invoke-VaultApi -Endpoint $Endpoint -QueryParams $QueryParams if ($Request.success) { if ($Request.data.data) { $Request.data.data } } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Collections & Organizations/Get-BwVaultOrgMembers.ps1' 37 #Region './Public/Vault API/Collections & Organizations/Get-BwVaultOrgs.ps1' 0 function Get-BwVaultOrgs { <# .SYNOPSIS Gets Bitwarden Vault Organizations .DESCRIPTION Calls /list/object/organizations .PARAMETER Search Organization name to search for .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding()] Param( $Search = '' ) $Endpoint = 'list/object/organizations' $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) if ($Search -ne '') { $QueryParams.Add('search', $Search) | Out-Null } $Request = Invoke-VaultApi -Endpoint $Endpoint -QueryParams $QueryParams if ($Request.success) { $Request.data.data $script:VaultOrgs = $Request.data.data } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Collections & Organizations/Get-BwVaultOrgs.ps1' 37 #Region './Public/Vault API/Collections & Organizations/Move-BwVaultItemToCollection.ps1' 0 function Move-BwVaultItemToCollection { <# .SYNOPSIS Moves Bitwarden Vault Item to Collection .DESCRIPTION Calls /move/{itemid}/{organizationid} .PARAMETER ItemId Guid of Item .PARAMETER OrganizationId Guid of Organization .PARAMETER CollectionIds List of CollectionIds .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(Mandatory = $true)] $ItemId, [Parameter(Mandatory = $true)] $OrganizationId, [Parameter(Mandatory = $true)] [string[]]$CollectionIds ) $Endpoint = 'move/{0}/{1}' -f $ItemId, $OrganizationId $Body = @($CollectionIds) | ConvertTo-Json $VaultApi = @{ Endpoint = $Endpoint Body = $Body Method = 'Post' } if ($PSCmdlet.ShouldProcess($ItemId)) { Invoke-VaultApi @VaultApi if ($Request.success) { $Request.data } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Collections & Organizations/Move-BwVaultItemToCollection.ps1' 52 #Region './Public/Vault API/Collections & Organizations/New-BwVaultOrgCollection.ps1' 0 function New-BwVaultOrgCollection { <# .SYNOPSIS Creates Bitwarden Vault Org Collections .DESCRIPTION Calls POST /object/org-collection to create new org collections .PARAMETER OrgCollection Full item object in PSCustomObject or json format .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] $OrganizationId, [Parameter(ValueFromPipeline, Mandatory = $true)] $OrgCollection ) Process { $OrgCollectonValid = $false if ($OrgCollection.GetType().Name -eq 'pscustomobject') { if ($OrgCollection.organizationId) { $OrganizationId = $OrgCollection.organizationId } $Body = $OrgCollection | ConvertTo-Json -Depth 10 $OrgCollectonValid = $true } elseif (Test-Json -Json $OrgCollection) { $Object = $OrgCollection | ConvertFrom-Json if ($Object.organizationId) { $OrganizationId = $Object.organizationId } $Body = $OrgCollection $OrgCollectonValid = $true } if (-not $OrganizationId -or -not $OrgCollectonValid) { Write-Error "Input validation failed for 'OrgCollection', valid types are pscustomobject or JSON string. An OrganizationId and an Id property must be specified" return } $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('organizationid', $OrganizationId) | Out-Null $VaultApi = @{ Method = 'POST' Endpoint = 'object/org-collection' Body = $Body QueryParams = $QueryParams } if ($PSCmdlet.ShouldProcess($OrgCollection.name)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } } #EndRegion './Public/Vault API/Collections & Organizations/New-BwVaultOrgCollection.ps1' 70 #Region './Public/Vault API/Collections & Organizations/Remove-BwVaultOrgCollection.ps1' 0 function Remove-BwVaultOrgCollection { <# .SYNOPSIS Deletes Bitwarden Org Collections Items .DESCRIPTION Calls DELETE /object/org-collection/{id} to move organization collections to the trash, this does not delete the items inside .PARAMETER Id OrgCollection guid .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $OrganizationId ) Process { $Endpoint = 'object/org-collection/{0}' -f $Id $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('organizationid', $OrganizationId) | Out-Null $VaultApi = @{ Method = 'Delete' Endpoint = $Endpoint QueryParams = $QueryParams } if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi @VaultApi } } } #EndRegion './Public/Vault API/Collections & Organizations/Remove-BwVaultOrgCollection.ps1' 39 #Region './Public/Vault API/Collections & Organizations/Update-BwVaultOrgCollection.ps1' 0 function Update-BwVaultOrgCollection { <# .SYNOPSIS Updates Bitwarden Vault Items .DESCRIPTION PUT /object/item/{id} .PARAMETER Id Item guid .PARAMETER Item Full item object in pscustoobject or json format .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'BodyUpdate', SupportsShouldProcess)] Param( [Parameter(ParameterSetName = 'BodyUpdate', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'BodyUpdate', Mandatory = $true)] $OrganizationId, [Parameter(ParameterSetName = 'BodyUpdate')] [Parameter(ValueFromPipeline = $true, ParameterSetName = 'FullObject')] $OrgCollection ) Process { $OrgCollectonValid = $false if ($OrgCollection.GetType().Name -eq 'pscustomobject') { if ($OrgCollection.id) { $Id = $OrgCollection.id } if ($OrgCollection.organizationId) { $OrganizationId = $OrgCollection.organizationId } $Body = $OrgCollection | ConvertTo-Json -Depth 10 $OrgCollectonValid = $true } elseif (Test-Json -Json $OrgCollection) { $Object = $OrgCollection | ConvertFrom-Json if ($Object.id) { $Id = $Object.id } $Body = $OrgCollection $OrgCollectonValid = $true } if (-not $Id -or -not $OrganizationId -or -not $OrgCollectonValid) { Write-Error "Input validation failed for 'OrgCollection', valid types are pscustomobject or JSON string. An OrganizationId and an Id property must be specified" return } Write-Verbose $Body $Endpoint = 'object/org-collection/{0}' -f $Id $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $QueryParams.Add('organizationid', $OrganizationId) | Out-Null $VaultApi = @{ Method = 'Put' Endpoint = $Endpoint Body = $Body QueryParams = $QueryParams } if ($OrgCollection) { $ProcessObject = '{0} ({1})' -f $OrgCollection.name, $Id } else { $ProcessObject = $Id } if ($PSCmdlet.ShouldProcess($ProcessObject)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } } #EndRegion './Public/Vault API/Collections & Organizations/Update-BwVaultOrgCollection.ps1' 85 #Region './Public/Vault API/Folders/Get-BwVaultFolder.ps1' 0 function Get-BwVaultFolder { <# .SYNOPSIS Gets Bitwarden Vault Folder .DESCRIPTION Calls /list/object/folders or /object/folder/{id} to retrieve vault folders .PARAMETER Id Folder guid .PARAMETER Search Search terms .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ParameterSetName = 'Single', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'List')] $Search = '' ) switch ($PSCmdlet.ParameterSetName) { 'List' { $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $Endpoint = 'list/object/folders' if ($Search -ne '') { $QueryParams.Add('search', $Search) | Out-Null } $VaultApi = @{ Endpoint = $Endpoint QueryParams = $QueryParams } } 'Single' { $Endpoint = 'object/folder/{0}' -f $Id $VaultApi = @{ Endpoint = $Endpoint } } } $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data.data) { $Request.data.data } elseif ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Folders/Get-BwVaultFolder.ps1' 62 #Region './Public/Vault API/Folders/New-BwVaultFolder.ps1' 0 function New-BwVaultFolder { <# .SYNOPSIS Creates Bitwarden Folder .DESCRIPTION Calls POST /object/folder to create new folders .PARAMETER Name Name of the folder .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipeline = $true, Mandatory = $true)] $Folder ) $FolderValid = $false if ($Folder.GetType().Name -eq 'pscustomobject') { $Body = $Folder | ConvertTo-Json -Depth 10 $FolderValid = $true } elseif (Test-Json -Json $Folder) { $Body = $Folder $FolderValid = $true } if (-not $FolderValid) { Write-Error "Input validation failed for 'Folder', valid types are pscustomobject or JSON string" return } $VaultApi = @{ Method = 'POST' Endpoint = 'object/folder' Body = $Body } if ($PSCmdlet.ShouldProcess($Folder.name)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Folders/New-BwVaultFolder.ps1' 56 #Region './Public/Vault API/Folders/Remove-BwVaultFolder.ps1' 0 function Remove-BwVaultFolder { <# .SYNOPSIS Deletes Bitwarden Vault Folder .DESCRIPTION Calls DELETE /object/item/{id} to move items to the trash .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Endpoint = 'object/folder/{0}' -f $Id $VaultApi = @{ Method = 'Delete' Endpoint = $Endpoint } if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi @VaultApi } } } #EndRegion './Public/Vault API/Folders/Remove-BwVaultFolder.ps1' 35 #Region './Public/Vault API/Folders/Update-BwVaultFolder.ps1' 0 function Update-BwVaultFolder { <# .SYNOPSIS Updates Bitwarden Vault Folder .DESCRIPTION PUT /object/folder/{id} .PARAMETER Id Item guid .PARAMETER Name Name of folder .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Name ) Process { $Body = @{ name = $Name } | ConvertTo-Json $Endpoint = 'object/folder/{0}' -f $Id $VaultApi = @{ Method = 'Put' Endpoint = $Endpoint Body = $Body } if ($PSCmdlet.ShouldProcess($Id)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } } #EndRegion './Public/Vault API/Folders/Update-BwVaultFolder.ps1' 54 #Region './Public/Vault API/Items/Get-BwVaultItem.ps1' 0 function Get-BwVaultItem { <# .SYNOPSIS Gets Bitwarden Vault Items .DESCRIPTION Calls /list/object/items or /object/item/{id} to retrieve vault items .PARAMETER Id Item guid .PARAMETER AsCredential Returns login property as credential object .PARAMETER OrganizationId Organization Guid .PARAMETER Organization Name of organization .PARAMETER CollectionId Collection Guid .PARAMETER Collection Name of collection .PARAMETER FolderId Folder Guid .PARAMETER Search Search terms .PARAMETER Url Search for matching Urls .PARAMETER Trash Show deleted items .LINK https://bitwarden.com/help/vault-management-api/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope = 'Function')] [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ParameterSetName = 'Single', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'Single')] [switch]$AsCredential, [Parameter(ParameterSetName = 'List')] $OrganizationId = '', [Parameter(ParameterSetName = 'List')] $Organization = '', [Parameter(ParameterSetName = 'List')] $CollectionId = '', [Parameter(ParameterSetName = 'List')] $Collection, [Parameter(ParameterSetName = 'List')] $FolderId = '', [Parameter(ParameterSetName = 'List')] $Search = '', [Parameter(ParameterSetName = 'List')] $Url = '', [Parameter(ParameterSetName = 'List')] [switch]$Trash ) switch ($PSCmdlet.ParameterSetName) { 'List' { if ($Collection) { if (!$script:VaultCollections) { Get-VaultCollections | Out-Null } $CollectionObj = $script:VaultCollections | Where-Object { $_.name -eq $Collection } $CollectionId = $CollectionObj.id Write-Verbose "Collection: $Collection ($CollectionId)" } if ($Organization) { if (!$script:VaultOrgs) { Get-VaultOrgs | Out-Null } $OrgObj = $script:VaultOrgs | Where-Object { $_.name -eq $Organization } $OrganizationId = $OrgObj.id Write-Verbose "Organization: $Organization ($OrganizationId)" } $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) $Endpoint = 'list/object/items' if ($Search -ne '') { $QueryParams.Add('search', $Search) | Out-Null } if ($OrganizationId -ne '') { $QueryParams.Add('organizationid', $OrganizationId) | Out-Null } if ($CollectionId -ne '') { $QueryParams.Add('collectionid', $CollectionId) | Out-Null } if ($FolderId -ne '') { $QueryParams.Add('folderid', $FolderId) | Out-Null } if ($Url -ne '') { $QueryParams.Add('url', $Url) | Out-Null } if ($Trash) { $QueryParams.Add('trash', $true) | Out-Null } $VaultApi = @{ Endpoint = $Endpoint QueryParams = $QueryParams } } 'Single' { $Endpoint = 'object/item/{0}' -f $Id $VaultApi = @{ Endpoint = $Endpoint } } } $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data.data) { $Request.data.data } elseif ($Request.data) { if ($AsCredential) { [securestring]$Password = ConvertTo-SecureString -String $Request.data.login.password -AsPlainText -Force New-Object System.Management.Automation.PSCredential ($Request.data.login.username, $Password) } else { $Request.data } } } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Items/Get-BwVaultItem.ps1' 151 #Region './Public/Vault API/Items/New-BwVaultItem.ps1' 0 function New-BwVaultItem { <# .SYNOPSIS Creates Bitwarden Vault Items .DESCRIPTION Calls POST /object/item to create vault items .PARAMETER Item Full item object in pscustoobject or json format .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipeline = $true, Mandatory = $true)] $Item ) Process { $ItemValid = $false if ($Item.GetType().Name -eq 'pscustomobject') { $Body = $Item | ConvertTo-Json -Depth 10 $ItemValid = $true } elseif (Test-Json -Json $Item) { $Body = $Item $ItemValid = $true } if (-not $ItemValid) { Write-Error "Input validation failed for 'Item', valid types are pscustomobject or JSON string and and id property must be specified" return } $VaultApi = @{ Method = 'POST' Endpoint = 'object/item' Body = $Body } if ($PSCmdlet.ShouldProcess($Item.name)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } } #EndRegion './Public/Vault API/Items/New-BwVaultItem.ps1' 58 #Region './Public/Vault API/Items/Remove-BwVaultItem.ps1' 0 function Remove-BwVaultItem { <# .SYNOPSIS Deletes Bitwarden Vault Items .DESCRIPTION Calls DELETE /object/item/{id} to move items to the trash .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Endpoint = 'object/item/{0}' -f $Id $VaultApi = @{ Method = 'Delete' Endpoint = $Endpoint } if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi @VaultApi } } } #EndRegion './Public/Vault API/Items/Remove-BwVaultItem.ps1' 34 #Region './Public/Vault API/Items/Restore-BwVaultItem.ps1' 0 function Restore-BwVaultItem { <# .SYNOPSIS Restores deleted Bitwarden Vault Items .DESCRIPTION PUT /restore/item/{id} .PARAMETER Id Item guid .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) Process { $Endpoint = 'restore/item/{0}' -f $Id $VaultApi = @{ Method = 'Post' Endpoint = $Endpoint Body = $Body } if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi @VaultApi } } } #EndRegion './Public/Vault API/Items/Restore-BwVaultItem.ps1' 36 #Region './Public/Vault API/Items/Update-BwVaultItem.ps1' 0 function Update-BwVaultItem { <# .SYNOPSIS Updates Bitwarden Vault Items .DESCRIPTION PUT /object/item/{id} .PARAMETER Id Item guid .PARAMETER Item Full item object in pscustoobject or json format .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'BodyUpdate', SupportsShouldProcess)] Param( [Parameter(ParameterSetName = 'BodyUpdate', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'BodyUpdate')] [Parameter(ValueFromPipeline = $true, ParameterSetName = 'FullObject')] $Item ) Process { $ItemValid = $false if ($Item.GetType().Name -eq 'pscustomobject') { $Body = $Item | ConvertTo-Json -Depth 10 $Id = $Item.id $ItemValid = $true } elseif (Test-Json -Json $Item) { $Object = $Item | ConvertFrom-Json $Id = $Object.id $Body = $Item $ItemValid = $true } if (-not $Id -or -not $ItemValid) { Write-Error "Input validation failed for 'Item', valid types are pscustomobject or JSON string and and id property must be specified" return } $Endpoint = 'object/item/{0}' -f $Id $VaultApi = @{ Method = 'Put' Endpoint = $Endpoint Body = $Body } if ($PSCmdlet.ShouldProcess($Id)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } } #EndRegion './Public/Vault API/Items/Update-BwVaultItem.ps1' 70 #Region './Public/Vault API/Lock & Unlock/Lock-BwVault.ps1' 0 function Lock-BwVault { <# .SYNOPSIS Locks Bitwarden Vault .DESCRIPTION Calls the /lock endpoint to clean up session .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param() $Request = Invoke-VaultApi -Endpoint 'lock' -Method Post [Environment]::SetEnvironmentVariable('BW_SESSION', $null) Write-Output $Request.data.title } #EndRegion './Public/Vault API/Lock & Unlock/Lock-BwVault.ps1' 19 #Region './Public/Vault API/Lock & Unlock/Unlock-BwVault.ps1' 0 function Unlock-BwVault { <# .SYNOPSIS Unlocks Bitwarden Vault .DESCRIPTION Calls the /unlock endpoint to open the vault .PARAMETER Credential Vault credential object for the master password .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess)] Param( [PSCredential]$Credential ) if ((Get-BwVaultStatus).status -ne 'unlocked') { if (!$Credential) { $Credential = Get-Credential -UserName 'Master Password' } $Body = @{ password = $Credential.GetNetworkCredential().Password } | ConvertTo-Json -Compress if ($PSCmdlet.ShouldProcess('Credential')) { $Request = Invoke-VaultApi -Endpoint 'unlock' -Method 'Post' -Body $Body # Set session variable for cli commands [Environment]::SetEnvironmentVariable('BW_SESSION', $Request.data.raw) if ($Request.success) { Write-Verbose $Request.data.title } else { Write-Verbose $Request.message } $Request.success } } else { $true } } #EndRegion './Public/Vault API/Lock & Unlock/Unlock-BwVault.ps1' 43 #Region './Public/Vault API/Miscellaneous/Get-BwVaultStatus.ps1' 0 function Get-BwVaultStatus { <# .SYNOPSIS Returns REST server status .DESCRIPTION Calls /status endpoint to check if vault is logged in .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param() $Request = Invoke-VaultApi -Endpoint 'status' $Request.data.template } #EndRegion './Public/Vault API/Miscellaneous/Get-BwVaultStatus.ps1' 18 #Region './Public/Vault API/Miscellaneous/Get-BwVaultTemplate.ps1' 0 function Get-BwVaultTemplate { <# .SYNOPSIS Returns vault template object .DESCRIPTION Calls /template/{type} endpoint .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param( [Parameter(Mandatory = $true)] [ValidateSet('item', 'item.field', 'item.login', 'item.login.uri', 'item.card', 'item.identity', 'item.securenote', 'folder', 'collection', 'item-collections', 'org-collection')] $Type ) #API method not available yet #Invoke-VaultApi -Endpoint ('template/{0}' -f $Type) $Arguments = @( 'get' 'template' $Type ) Invoke-VaultCli -Arguments $Arguments } #EndRegion './Public/Vault API/Miscellaneous/Get-BwVaultTemplate.ps1' 31 #Region './Public/Vault API/Miscellaneous/New-BwPassword.ps1' 0 function New-BwPassword { <# .SYNOPSIS Creates a new password .DESCRIPTION Calls the /generate endpoint to create a new password .LINK https://bitwarden.com/help/vault-management-api/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope = 'Function')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function')] [cmdletbinding()] Param() $Request = Invoke-VaultApi -Endpoint 'generate' if ($Request) { $Request.data.data | ConvertTo-SecureString -AsPlainText -Force } else { Write-Error 'Unable to generate password' } } #EndRegion './Public/Vault API/Miscellaneous/New-BwPassword.ps1' 24 #Region './Public/Vault API/Miscellaneous/Sync-BwVault.ps1' 0 function Sync-BwVault { <# .SYNOPSIS Syncs local Bitwarden vault with server .DESCRIPTION Calls /sync endpoint to synchronize local vault .LINK https://bitwarden.com/help/vault-management-api/ #> [cmdletbinding()] Param() $Request = Invoke-VaultApi -Endpoint 'sync' -Method Post $Request.data } #EndRegion './Public/Vault API/Miscellaneous/Sync-BwVault.ps1' 18 #Region './Public/Vault API/REST/Start-BwRestServer.ps1' 0 function Start-BwRestServer { <# .SYNOPSIS Starts Bitwarden REST server .DESCRIPTION Uses `bw serve` to run local REST server .PARAMETER Port Port to run server on, default 8087 .PARAMETER Hostname Hostname to run server on, default localhost .LINK https://bitwarden.com/help/cli/#serve #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function')] [cmdletbinding()] Param( $Port = 8087, $Hostname = '127.0.0.1' ) try { if (!$script:BwRestServer) { $BwServe = Get-Process bw -ErrorAction SilentlyContinue $TestPort = Test-Connection -IPv4 -TargetName $Hostname -TcpPort $Port if ($BwServe -and $TestPort) { Write-Verbose 'REST server already running' $RunningCli = $BwServe $script:BwRestServer = [PSCustomObject]@{ PID = $BwServe.Id Port = $Port Hostname = $Hostname } } else { $RunningCli = $false } } else { $RunningCli = Get-Process -PID $script:BwRestServer.PID -ErrorAction SilentlyContinue } } catch { Stop-RestServer $RunningCli = $false } if (-not $RunningCli) { $Arguments = @( 'serve' "--port $Port" "--hostname $Hostname" ) try { $bw = Get-Command bw if (!$bw) { Write-Error 'Bitwarden CLI is not installed, visit https://bitwarden.com/help/cli/#download-and-install for more information.' return $false } Write-Verbose 'Starting REST server' $Proc = Start-Process -FilePath $bw.Path -ArgumentList $Arguments -NoNewWindow -PassThru -ErrorAction Stop do { $VaultRest = Test-Connection -IPv4 -TargetName $Hostname -TcpPort $Port Start-Sleep -Milliseconds 200 } while (-not $VaultRest) $script:BwRestServer = [PSCustomObject]@{ PID = $Proc.Id Port = $Port Hostname = $Hostname } $script:BwRestServer } catch { Write-Error "Could not start REST server. Exception: $($_.Exception.Message)" } } } #EndRegion './Public/Vault API/REST/Start-BwRestServer.ps1' 80 #Region './Public/Vault API/REST/Stop-BwRestServer.ps1' 0 function Stop-RestServer { <# .SYNOPSIS Stops Bitwarden REST server .DESCRIPTION Stops bw process if found running .LINK https://bitwarden.com/help/cli/#serve #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function')] [cmdletbinding()] Param() $RunningCli = Get-Process bw -ErrorAction SilentlyContinue if ($RunningCli) { Write-Verbose 'Stopping REST server' $RunningCli | Stop-Process $script:BwRestServer = $null } } #EndRegion './Public/Vault API/REST/Stop-BwRestServer.ps1' 24 #Region './Public/Vault API/Send/Get-BwSend.ps1' 0 function Get-BwSend { <# .SYNOPSIS Gets Bitwarden Sends .DESCRIPTION Calls /list/object/send or /object/send/{id} .PARAMETER Id Guid of Collection .PARAMETER Search Search parameters .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ParameterSetName = 'Single', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'List')] $Search = '' ) switch ($PSCmdlet.ParameterSetName) { 'List' { $Endpoint = 'list/object/send' $QueryParams = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) if ($Search -ne '') { $QueryParams.Add('search', $Search) | Out-Null } } 'Single' { $Endpoint = 'object/send/{0}' -f $Id } } $Request = Invoke-VaultApi -Endpoint $Endpoint -QueryParams $QueryParams if ($Request.success) { if ($Request.data.data) { $Request.data.data } elseif ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } #EndRegion './Public/Vault API/Send/Get-BwSend.ps1' 54 #Region './Public/Vault API/Send/New-BwSend.ps1' 0 function New-BwSend { <# .SYNOPSIS Creates Bitwarden Send .DESCRIPTION Calls POST /object/send to create a new send .PARAMETER OrgCollection Full item object in pscustoobject or json format .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'SendParams', SupportsShouldProcess)] Param( [Parameter(ParameterSetName = 'SendParams', Mandatory = $true)] $Name, [Parameter(ParameterSetName = 'SendParams')] $Notes = '', [Parameter(ParameterSetName = 'SendParams')] $SendPass = '', [Parameter(ParameterSetName = 'SendParams', Mandatory = $true)] $Text, [Parameter(ParameterSetName = 'SendParams')] [int]$Days = 7, [Parameter(ParameterSetName = 'SendParams')] [int]$MaxAccessCount = 3, [Parameter(ParameterSetName = 'SendParams')] [switch]$HideText, [Parameter(ParameterSetName = 'SendParams')] [switch]$HideEmail, [Parameter(ParameterSetName = 'FullObject', ValueFromPipeline, Mandatory = $true)] $Send ) $SendValid = $false switch ($PSCmdlet.ParameterSetName) { 'FullObject' { if ($Send.GetType().Name -eq 'pscustomobject') { $Body = $Send | ConvertTo-Json -Depth 10 $SendValid = $true } elseif (Test-Json -Json $Send) { $Body = $Send $SendValid = $true } if (-not $SendValid) { Write-Error "Input validation failed for 'Send', valid types are pscustomobject or JSON string" return } } 'SendParams' { $Send = [pscustomobject]@{ name = $Name notes = $Notes type = 0 text = @{ text = $Text hidden = $HideText.IsPresent } file = $null maxAccessCount = $MaxAccessCount deletionDate = (Get-Date).AddDays($Days).ToUniversalTime() expirationDate = (Get-Date).AddDays($Days).ToUniversalTime() hideEmail = $HideEmail.IsPresent disabled = $false } if ($SendPass) { $Send.password = $SendPass } $Body = $Send | ConvertTo-Json Write-Verbose $Body } } $VaultApi = @{ Method = 'POST' Endpoint = 'object/send' Body = $Body } if ($PSCmdlet.ShouldProcess($Name)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Send/New-BwSend.ps1' 97 #Region './Public/Vault API/Send/Remove-BwSend.ps1' 0 function Remove-BwSend { <# .SYNOPSIS Deletes Bitwarden Send .DESCRIPTION Calls DELETE /object/send/{id} to move items to the trash .PARAMETER Id Send guid .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] $Id ) process { $Endpoint = 'object/send/{0}' -f $Id $VaultApi = @{ Method = 'Delete' Endpoint = $Endpoint } if ($PSCmdlet.ShouldProcess($Id)) { Invoke-VaultApi @VaultApi } } } #EndRegion './Public/Vault API/Send/Remove-BwSend.ps1' 35 #Region './Public/Vault API/Send/Update-BwSend.ps1' 0 function Update-BwSend { <# .SYNOPSIS Updates Bitwarden Send .DESCRIPTION PUT /object/send/{id} .PARAMETER Id Send guid .PARAMETER Send Full Send object in pscustoobject or json format .LINK https://bitwarden.com/help/vault-management-api/ #> [CmdletBinding(DefaultParameterSetName = 'BodyUpdate', SupportsShouldProcess)] Param( [Parameter(ParameterSetName = 'BodyUpdate', Mandatory = $true)] $Id, [Parameter(ParameterSetName = 'BodyUpdate')] [Parameter(ValueFromPipeline, ParameterSetName = 'FullObject')] $Send ) $SendValid = $false if ($Send.GetType().Name -eq 'pscustomobject') { $Body = $Send | ConvertTo-Json -Depth 10 $Id = $Send.id $SendValid = $true } elseif (Test-Json -Json $Send) { $Object = $Send | ConvertFrom-Json $Id = $Object.id $Body = $Send $SendValid = $true } if (-not $Id -or -not $SendValid) { Write-Error "Input validation failed for 'Item', valid types are pscustomobject or JSON string and and id property must be specified" return } $Endpoint = 'object/send/{0}' -f $Id $VaultApi = @{ Method = 'Put' Endpoint = $Endpoint Body = $Body } if ($PSCmdlet.ShouldProcess($Id)) { $Request = Invoke-VaultApi @VaultApi if ($Request.success) { if ($Request.data) { $Request.data } } else { Write-Verbose $Request.message $Request.success } } } #EndRegion './Public/Vault API/Send/Update-BwSend.ps1' 68 |