Public/user.ps1

function New-CSUser {
    <#
    .SYNOPSIS
        Creates a user for an existing CloudStack account.

    .DESCRIPTION
        Wraps the createUser API. Adds a user to an existing account in a domain. All
        of the email, name, username, and password values are required.

    .PARAMETER Account
        Name of the account the user is added to.

    .PARAMETER Email
        Email address of the user.

    .PARAMETER FirstName
        First name of the user.

    .PARAMETER LastName
        Last name of the user.

    .PARAMETER Password
        Password for the user. Supply this from a secure source (for example a secret
        store or a decrypted PSCredential) rather than a literal string.

    .PARAMETER UserName
        Login name for the user.

    .PARAMETER DomainId
        Domain of the account. Defaults to ROOT when omitted.

    .PARAMETER Timezone
        Time zone for the user (for example 'America/Detroit').

    .PARAMETER UserId
        Assign a specific user UUID instead of letting CloudStack generate one.

    .EXAMPLE
        New-CSUser -Account 'engineering' -Email 'ada@example.com' -FirstName 'Ada' -LastName 'Lovelace' -UserName 'ada' -Password 'use-a-secret-store'
        Adds a user to the engineering account.

    .EXAMPLE
        New-CSUser -Account 'engineering' -Email 'ops@example.com' -FirstName 'Ops' -LastName 'Team' -UserName 'ops' -Password $securePasswordPlain -DomainId $domainId -Timezone 'America/Detroit'
        Adds a user in a specific domain with a time zone.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)][string]$Account,
        [Parameter(Mandatory=$true)][string]$Email,
        [Parameter(Mandatory=$true)][string]$FirstName,
        [Parameter(Mandatory=$true)][string]$LastName,
        [Parameter(Mandatory=$true)][string]$Password,
        [Parameter(Mandatory=$true)][string]$UserName,
        [string]$DomainId,
        [string]$Timezone,
        [string]$UserId
    )
    $apiParams = @{ account = $Account; email = $Email; firstname = $FirstName; lastname = $LastName; password = $Password; username = $UserName }
    $parameterMap = @{ DomainId = 'domainid'; Timezone = 'timezone'; UserId = 'userid' }
    foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
    Invoke-CSApiRequest -Command 'createUser' -Parameters $apiParams
}

function Remove-CSUser {
    <#
    .SYNOPSIS
        Deletes a CloudStack user.

    .DESCRIPTION
        Wraps the deleteUser API. Permanently removes a user. Accepts a user object
        (or its id) from the pipeline. This is destructive, so it honors
        -WhatIf/-Confirm.

    .PARAMETER UserId
        The user UUID to delete. Binds from a piped object's Id property.

    .EXAMPLE
        Remove-CSUser -UserId 'user-uuid' -Confirm:$false
        Deletes a user without prompting.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Remove-CSUser
        Pipes a user in and deletes it after confirmation.
    #>

    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId)
    process {
        if ($PSCmdlet.ShouldProcess("user $UserId", 'Delete')) { Invoke-CSApiRequest -Command 'deleteUser' -Parameters @{ id = $UserId } }
    }
}

function Disable-CSUser {
    <#
    .SYNOPSIS
        Disables a CloudStack user.

    .DESCRIPTION
        Wraps the disableUser API. Prevents the user from logging in or making API
        calls until re-enabled. Accepts a user object (or its id) from the pipeline.
        Honors -WhatIf/-Confirm.

    .PARAMETER UserId
        The user UUID to disable. Binds from a piped object's Id property.

    .EXAMPLE
        Disable-CSUser -UserId 'user-uuid'
        Disables a user.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Disable-CSUser
        Disables a piped user.
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId)
    process {
        if ($PSCmdlet.ShouldProcess("user $UserId", 'Disable')) { Invoke-CSApiRequest -Command 'disableUser' -Parameters @{ id = $UserId } }
    }
}

function Enable-CSUser {
    <#
    .SYNOPSIS
        Enables a CloudStack user.

    .DESCRIPTION
        Wraps the enableUser API. Restores login and API access for a disabled user.
        Accepts a user object (or its id) from the pipeline.

    .PARAMETER UserId
        The user UUID to enable. Binds from a piped object's Id property.

    .EXAMPLE
        Enable-CSUser -UserId 'user-uuid'
        Re-enables a user.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Enable-CSUser
        Enables a piped user.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId)
    process {
        Invoke-CSApiRequest -Command 'enableUser' -Parameters @{ id = $UserId }
    }
}

function Get-CSUserByApiKey {
    <#
    .SYNOPSIS
        Finds a CloudStack user by API key.

    .DESCRIPTION
        Wraps the getUser API. Looks up the user that owns a given API key - useful
        for identifying which user a set of credentials belongs to.

    .PARAMETER UserApiKey
        The API key to look up.

    .EXAMPLE
        Get-CSUserByApiKey -UserApiKey 'api-key'
        Returns the user that owns the given API key.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true)][string]$UserApiKey)
    $response = Invoke-CSApiRequest -Command 'getUser' -Parameters @{ userapikey = $UserApiKey }
    if ($response.getuserresponse.user) { return $response.getuserresponse.user }
    return $response
}

function Get-CSUserKeys {
    <#
    .SYNOPSIS
        Gets a user's API and secret keys.

    .DESCRIPTION
        Wraps the getUserKeys API. Returns the API key and secret key registered for
        a user. Treat the returned secret key as sensitive - avoid logging it.

    .PARAMETER UserId
        The user UUID whose keys to retrieve.

    .EXAMPLE
        Get-CSUserKeys -UserId 'user-uuid'
        Returns the user's API and secret keys.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true)][string]$UserId)
    Invoke-CSApiRequest -Command 'getUserKeys' -Parameters @{ id = $UserId }
}

function Get-CSUserTwoFactorAuthenticatorProvider {
    <#
    .SYNOPSIS
        Lists available two-factor-authentication providers.

    .DESCRIPTION
        Wraps the listUserTwoFactorAuthenticatorProviders API. Returns the 2FA
        providers configured on the server (for example totp, staticpin), optionally
        filtered by name.

    .PARAMETER Name
        Filter to a single provider by name.

    .EXAMPLE
        Get-CSUserTwoFactorAuthenticatorProvider
        Lists all 2FA providers.

    .EXAMPLE
        Get-CSUserTwoFactorAuthenticatorProvider -Name 'totp'
        Gets details for the TOTP provider.
    #>

    [CmdletBinding()]
    param([string]$Name)
    $apiParams = @{}
    if ($PSBoundParameters.ContainsKey('Name')) { $apiParams['name'] = $Name }
    $response = Invoke-CSApiRequest -Command 'listUserTwoFactorAuthenticatorProviders' -Parameters $apiParams
    if ($response.listusertwofactorauthenticatorprovidersresponse.provider) { return $response.listusertwofactorauthenticatorprovidersresponse.provider }
    return $response
}

function Get-CSUser {
    <#
    .SYNOPSIS
        Lists CloudStack users.

    .DESCRIPTION
        Wraps the listUsers API. Returns users filtered by account, domain, id, name,
        or state. Use -ListAll (admin) to include users across all domains and
        -IsRecursive to include subdomains. Returns nothing when no user matches.

    .PARAMETER Account
        Filter by account name.

    .PARAMETER AccountType
        Filter by account type: admin, domain-admin, read-only-admin, or user.

    .PARAMETER DomainId
        Filter by domain.

    .PARAMETER Id
        Filter by user UUID.

    .PARAMETER IsRecursive
        Include users from subdomains of -DomainId.

    .PARAMETER Keyword
        Filter by a keyword substring match.

    .PARAMETER ListAll
        List users across all domains the caller can see (admin).

    .PARAMETER Page
        Page number for paged results.

    .PARAMETER PageSize
        Number of results per page.

    .PARAMETER ShowIcon
        Include each user's resource icon in the response.

    .PARAMETER State
        Filter by state (for example enabled, disabled, locked).

    .PARAMETER UserName
        Filter by login name.

    .EXAMPLE
        Get-CSUser -Account 'engineering' -DomainId 'domain-uuid' -State enabled
        Lists enabled users in an account.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId
        Gets a single user by login name and domain.
    #>

    [CmdletBinding()]
    param(
        [string]$Account,
        [ValidateSet('admin', 'domain-admin', 'read-only-admin', 'user')][string]$AccountType,
        [string]$DomainId,
        [string]$Id,
        [switch]$IsRecursive,
        [string]$Keyword,
        [switch]$ListAll,
        [int]$Page,
        [int]$PageSize,
        [switch]$ShowIcon,
        [string]$State,
        [string]$UserName
    )
    $apiParams = @{}
    $parameterMap = @{ Account = 'account'; AccountType = 'accounttype'; DomainId = 'domainid'; Id = 'id'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'; State = 'state'; UserName = 'username' }
    foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
    if ($IsRecursive) { $apiParams['isrecursive'] = 'true' }
    if ($ListAll) { $apiParams['listall'] = 'true' }
    if ($ShowIcon) { $apiParams['showicon'] = 'true' }
    $response = Invoke-CSApiRequest -Command 'listUsers' -Parameters $apiParams
    if ($response.listusersresponse.user) { return $response.listusersresponse.user }
    Write-Verbose 'No users found matching the criteria.'
}

function Lock-CSUser {
    <#
    .SYNOPSIS
        Locks a CloudStack user.

    .DESCRIPTION
        Wraps the lockUser API. Blocks the user from logging in while leaving the
        account otherwise intact. Accepts a user object (or its id) from the pipeline.
        Honors -WhatIf/-Confirm.

    .PARAMETER UserId
        The user UUID to lock. Binds from a piped object's Id property.

    .EXAMPLE
        Lock-CSUser -UserId 'user-uuid'
        Locks a user.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Lock-CSUser
        Locks a piped user.
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId)
    process {
        if ($PSCmdlet.ShouldProcess("user $UserId", 'Lock')) { Invoke-CSApiRequest -Command 'lockUser' -Parameters @{ id = $UserId } }
    }
}

function Move-CSUser {
    <#
    .SYNOPSIS
        Moves a user to another account in the same domain.

    .DESCRIPTION
        Wraps the moveUser API. Reassigns a user to a different account within the
        same domain. Identify the destination by -Account (name) or -AccountId, but
        not both. Accepts a user object (or its id) from the pipeline.

    .PARAMETER UserId
        The user UUID to move. Binds from a piped object's Id property.

    .PARAMETER Account
        Destination account name. Mutually exclusive with -AccountId.

    .PARAMETER AccountId
        Destination account UUID. Mutually exclusive with -Account.

    .EXAMPLE
        Move-CSUser -UserId 'user-uuid' -Account 'platform'
        Moves a user into the platform account by name.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Move-CSUser -AccountId $accountId
        Moves a piped user into an account by id.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId, [string]$Account, [string]$AccountId)
    process {
        if (-not $PSBoundParameters.ContainsKey('Account') -and -not $PSBoundParameters.ContainsKey('AccountId')) { throw 'Specify Account or AccountId.' }
        if ($PSBoundParameters.ContainsKey('Account') -and $PSBoundParameters.ContainsKey('AccountId')) { throw 'Specify either Account or AccountId, not both.' }
        $apiParams = @{ id = $UserId }
        if ($PSBoundParameters.ContainsKey('Account')) { $apiParams['account'] = $Account } else { $apiParams['accountid'] = $AccountId }
        Invoke-CSApiRequest -Command 'moveUser' -Parameters $apiParams
    }
}

function Register-CSUserKeys {
    <#
    .SYNOPSIS
        Registers API keys for a user through the integration API.

    .DESCRIPTION
        Wraps the registerUserKeys API. Generates (or regenerates) the API key and
        secret key for a user and returns them. Accepts a user object (or its id) from
        the pipeline. Treat the returned secret key as sensitive.

    .PARAMETER UserId
        The user UUID to register keys for. Binds from a piped object's Id property.

    .EXAMPLE
        Register-CSUserKeys -UserId 'user-uuid'
        Generates and returns a new API/secret key pair for the user.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Register-CSUserKeys
        Registers keys for a piped user.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId)
    process {
        Invoke-CSApiRequest -Command 'registerUserKeys' -Parameters @{ id = $UserId }
    }
}

function Set-CSUserTwoFactorAuthentication {
    <#
    .SYNOPSIS
        Configures two-factor authentication for a user.

    .DESCRIPTION
        Wraps the setupUserTwoFactorAuthentication API. Enables (default) or disables
        2FA for a user and, when enabling, selects the provider. With no -UserId it
        applies to the calling user.

    .PARAMETER Enable
        Whether to enable (default $true) or disable 2FA.

    .PARAMETER Provider
        The 2FA provider to use when enabling (for example totp).

    .PARAMETER UserId
        The user UUID to configure. Defaults to the calling user when omitted.

    .EXAMPLE
        Set-CSUserTwoFactorAuthentication -UserId 'user-uuid' -Provider 'totp'
        Enables TOTP 2FA for a user.

    .EXAMPLE
        Set-CSUserTwoFactorAuthentication -UserId 'user-uuid' -Enable $false
        Disables 2FA for a user.
    #>

    [CmdletBinding()]
    param([bool]$Enable = $true, [string]$Provider, [string]$UserId)
    $apiParams = @{ enable = $Enable.ToString().ToLowerInvariant() }
    if ($PSBoundParameters.ContainsKey('Provider')) { $apiParams['provider'] = $Provider }
    if ($PSBoundParameters.ContainsKey('UserId')) { $apiParams['userid'] = $UserId }
    Invoke-CSApiRequest -Command 'setupUserTwoFactorAuthentication' -Parameters $apiParams
}

function Set-CSUser {
    <#
    .SYNOPSIS
        Updates a CloudStack user.

    .DESCRIPTION
        Wraps the updateUser API. Changes a user's profile (email, name, time zone,
        login), password, API/secret key pair, or 2FA mandate. -UserApiKey and
        -UserSecretKey must be supplied together. Accepts a user object (or its id)
        from the pipeline. Supply any password or key from a secure source.

    .PARAMETER UserId
        The user UUID to update. Binds from a piped object's Id property.

    .PARAMETER CurrentPassword
        The user's current password, required by some deployments when changing the
        password.

    .PARAMETER Email
        New email address.

    .PARAMETER FirstName
        New first name.

    .PARAMETER LastName
        New last name.

    .PARAMETER MandateTwoFactorAuthentication
        Whether to require the user to set up 2FA.

    .PARAMETER Password
        New password. Supply from a secure source rather than a literal string.

    .PARAMETER Timezone
        New time zone.

    .PARAMETER UserApiKey
        New API key. Must be supplied together with -UserSecretKey.

    .PARAMETER UserName
        New login name.

    .PARAMETER UserSecretKey
        New secret key. Must be supplied together with -UserApiKey; supply from a
        secure source.

    .EXAMPLE
        Set-CSUser -UserId 'user-uuid' -Email 'ada.lovelace@example.com' -Timezone 'America/Detroit'
        Updates a user's email and time zone.

    .EXAMPLE
        Get-CSUser -UserName 'ada' -DomainId $domainId | Set-CSUser -MandateTwoFactorAuthentication $true
        Requires a piped user to set up 2FA.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserId,
        [string]$CurrentPassword,
        [string]$Email,
        [string]$FirstName,
        [string]$LastName,
        [Nullable[bool]]$MandateTwoFactorAuthentication,
        [string]$Password,
        [string]$Timezone,
        [string]$UserApiKey,
        [string]$UserName,
        [string]$UserSecretKey
    )
    process {
        if ($PSBoundParameters.ContainsKey('UserApiKey') -xor $PSBoundParameters.ContainsKey('UserSecretKey')) { throw 'UserApiKey and UserSecretKey must be specified together.' }
        $apiParams = @{ id = $UserId }
        $parameterMap = @{ CurrentPassword = 'currentpassword'; Email = 'email'; FirstName = 'firstname'; LastName = 'lastname'; Password = 'password'; Timezone = 'timezone'; UserApiKey = 'userapikey'; UserName = 'username'; UserSecretKey = 'usersecretkey' }
        foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
        if ($PSBoundParameters.ContainsKey('MandateTwoFactorAuthentication')) { $apiParams['mandate2fa'] = ([bool]$MandateTwoFactorAuthentication).ToString().ToLowerInvariant() }
        Invoke-CSApiRequest -Command 'updateUser' -Parameters $apiParams
    }
}

function Test-CSUserTwoFactorAuthenticationCode {
    <#
    .SYNOPSIS
        Validates the current user's two-factor-authentication code.

    .DESCRIPTION
        Wraps the validateUserTwoFactorAuthenticationCode API. Verifies a 2FA code for
        the calling user as part of the login/setup flow.

    .PARAMETER Code
        The 2FA code to validate.

    .EXAMPLE
        Test-CSUserTwoFactorAuthenticationCode -Code '123456'
        Validates a 2FA code for the current user.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true)][string]$Code)
    Invoke-CSApiRequest -Command 'validateUserTwoFactorAuthenticationCode' -Parameters @{ codefor2fa = $Code }
}