diff --git a/src/PSProxmoxVE.Core/Models/Users/PveApiToken.cs b/src/PSProxmoxVE.Core/Models/Users/PveApiToken.cs
index 44a0c8e..676f60a 100644
--- a/src/PSProxmoxVE.Core/Models/Users/PveApiToken.cs
+++ b/src/PSProxmoxVE.Core/Models/Users/PveApiToken.cs
@@ -23,10 +23,20 @@ public class PveApiToken
///
/// The full token identifier in "user@realm!tokenid" format, e.g., "admin@pam!automation".
- /// Only populated by New-PveApiToken; not returned by the list/get endpoints.
+ /// Computed from UserId and TokenId. The "full-tokenid" JSON field from New-PveApiToken
+ /// is captured by for deserialization, but this property
+ /// always returns a computed value so it works for list/get endpoints too.
+ ///
+ public string FullTokenId =>
+ string.IsNullOrEmpty(UserId) || string.IsNullOrEmpty(TokenId)
+ ? RawFullTokenId ?? string.Empty
+ : $"{UserId}!{TokenId}";
+
+ ///
+ /// Raw "full-tokenid" value from the API (only present on token creation responses).
///
[JsonProperty("full-tokenid")]
- public string? FullTokenId { get; set; }
+ public string? RawFullTokenId { get; set; }
///
/// The token secret UUID. Only present on creation — store it immediately,
diff --git a/src/PSProxmoxVE.Core/Services/UserService.cs b/src/PSProxmoxVE.Core/Services/UserService.cs
index f3b6af3..e677900 100644
--- a/src/PSProxmoxVE.Core/Services/UserService.cs
+++ b/src/PSProxmoxVE.Core/Services/UserService.cs
@@ -642,6 +642,7 @@ namespace PSProxmoxVE.Core.Services
/// Comma-separated role IDs.
/// Comma-separated user IDs.
/// Comma-separated group names.
+ /// Comma-separated API token IDs (user@realm!tokenid).
/// Whether to propagate the permission to sub-paths.
/// If true, removes the specified ACL entries.
public void SetPermission(
@@ -650,6 +651,7 @@ namespace PSProxmoxVE.Core.Services
string roles,
string? users = null,
string? groups = null,
+ string? tokens = null,
bool propagate = true,
bool delete = false)
{
@@ -666,6 +668,7 @@ namespace PSProxmoxVE.Core.Services
};
if (!string.IsNullOrEmpty(users)) formData["users"] = users!;
if (!string.IsNullOrEmpty(groups)) formData["groups"] = groups!;
+ if (!string.IsNullOrEmpty(tokens)) formData["tokens"] = tokens!;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
diff --git a/src/PSProxmoxVE/Cmdlets/Connection/ConnectPveServerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Connection/ConnectPveServerCmdlet.cs
index 6cca5d3..f311b49 100644
--- a/src/PSProxmoxVE/Cmdlets/Connection/ConnectPveServerCmdlet.cs
+++ b/src/PSProxmoxVE/Cmdlets/Connection/ConnectPveServerCmdlet.cs
@@ -48,10 +48,14 @@ namespace PSProxmoxVE.Cmdlets.Connection
[Parameter(Mandatory = false, HelpMessage = "Skip TLS certificate validation.")]
public SwitchParameter SkipCertificateCheck { get; set; }
- /// When specified, writes the resulting PveSession object to the pipeline.
- [Parameter(Mandatory = false, HelpMessage = "Output the session object to the pipeline.")]
+ /// Deprecated — session is now always output. Kept for backwards compatibility.
+ [Parameter(Mandatory = false, DontShow = true)]
public SwitchParameter PassThru { get; set; }
+ /// When specified, suppresses the session object from the pipeline output.
+ [Parameter(Mandatory = false, HelpMessage = "Do not output the session object to the pipeline.")]
+ public SwitchParameter Quiet { get; set; }
+
protected override void ProcessRecord()
{
PveSession session;
@@ -122,7 +126,7 @@ namespace PSProxmoxVE.Cmdlets.Connection
WriteVerbose($"Connected to {Server}:{Port} as {session.AuthMode} (PVE {session.ServerVersion}).");
- if (PassThru.IsPresent)
+ if (!Quiet.IsPresent)
WriteObject(session);
}
}
diff --git a/src/PSProxmoxVE/Cmdlets/Users/SetPvePermissionCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Users/SetPvePermissionCmdlet.cs
index 515ea7c..0d54564 100644
--- a/src/PSProxmoxVE/Cmdlets/Users/SetPvePermissionCmdlet.cs
+++ b/src/PSProxmoxVE/Cmdlets/Users/SetPvePermissionCmdlet.cs
@@ -27,9 +27,10 @@ namespace PSProxmoxVE.Cmdlets.Users
[Parameter(Mandatory = true, Position = 2, HelpMessage = "The role to assign (e.g. Administrator).")]
public string Role { get; set; } = string.Empty;
- /// The ACL entry type: "user" or "group".
- [Parameter(Mandatory = false, HelpMessage = "ACL entry type: user or group.")]
- [ValidateSet("user", "group", IgnoreCase = true)]
+ /// The ACL entry type: "user", "token", or "group". When set to "user", API tokens
+ /// (UgId containing "!") are automatically detected and sent as the "tokens" parameter.
+ [Parameter(Mandatory = false, HelpMessage = "ACL entry type: user, token, or group.")]
+ [ValidateSet("user", "token", "group", IgnoreCase = true)]
public string Type { get; set; } = "user";
/// Whether to propagate this ACL to child paths.
@@ -58,6 +59,8 @@ namespace PSProxmoxVE.Cmdlets.Users
if (string.Equals(Type, "group", System.StringComparison.OrdinalIgnoreCase))
data["groups"] = UgId;
+ else if (string.Equals(Type, "token", System.StringComparison.OrdinalIgnoreCase) || UgId.Contains("!"))
+ data["tokens"] = UgId;
else
data["users"] = UgId;
diff --git a/tests/PSProxmoxVE.Tests/Connection/Connect-PveServer.Tests.ps1 b/tests/PSProxmoxVE.Tests/Connection/Connect-PveServer.Tests.ps1
index af46b4c..7c0c13e 100644
--- a/tests/PSProxmoxVE.Tests/Connection/Connect-PveServer.Tests.ps1
+++ b/tests/PSProxmoxVE.Tests/Connection/Connect-PveServer.Tests.ps1
@@ -94,12 +94,18 @@ Describe 'Connect-PveServer' {
Should -Be ([System.Management.Automation.SwitchParameter])
}
- It 'Should have a PassThru switch parameter' {
+ It 'Should have a PassThru switch parameter (deprecated, hidden)' {
$script:Cmd.Parameters.ContainsKey('PassThru') | Should -BeTrue
$script:Cmd.Parameters['PassThru'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
+ It 'Should have a Quiet switch parameter' {
+ $script:Cmd.Parameters.ContainsKey('Quiet') | Should -BeTrue
+ $script:Cmd.Parameters['Quiet'].ParameterType |
+ Should -Be ([System.Management.Automation.SwitchParameter])
+ }
+
It 'Credential and ApiToken should belong to different parameter sets' {
$credSets = $script:Cmd.Parameters['Credential'].ParameterSets.Keys
$tokenSets = $script:Cmd.Parameters['ApiToken'].ParameterSets.Keys