src/users.psm1

Set-StrictMode -Version Latest

# Load common code
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
. "$here\common.ps1"

# Apply types to the returned objects so format and type files can
# identify the object and act on it.
function _applyTypesToUser {
    param(
        [Parameter(Mandatory = $true)]
        $item
    )

    $item.PSObject.TypeNames.Insert(0, 'Team.UserEntitlement')
    $item.accessLevel.PSObject.TypeNames.Insert(0, 'Team.AccessLevel')
}

function _supportsMemberEntitlementManagement {
    if (-not [VSTeamVersions]::MemberEntitlementManagement) {
        throw 'This account does not support Member Entitlement.'
    } 
}

function Get-VSTeamUser {
    [CmdletBinding(DefaultParameterSetName = 'List')]
    param (
        [Parameter(ParameterSetName = 'List')]
        [int] $Top = 100,
 
        [Parameter(ParameterSetName = 'List')]
        [int] $Skip = 0,

        [Parameter(ParameterSetName = 'List')]
        [ValidateSet('Projects', 'Extensions', 'Grouprules')]
        [string[]] $Select,
 
        [Parameter(ParameterSetName = 'ByID')]
        [Alias('UserId')]
        [string[]] $Id
    )

    process {
        # Thi swill throw if this account does not support MemberEntitlementManagement
        _supportsMemberEntitlementManagement

        if ($Id) {
            foreach ($item in $Id) {
                # Build the url to return the single build
                # Call the REST API
                $resp = _callAPI -SubDomain 'vsaex' -Version $([VSTeamVersions]::MemberEntitlementManagement) -Resource 'userentitlements' -id $item
            
                _applyTypesToUser -item $resp

                Write-Output $resp
            }
        }
        else {
            # Build the url to list the teams
            # $listurl = _buildUserURL
            $listurl = _buildRequestURI -SubDomain 'vsaex' -Resource 'userentitlements' `
                -Version $([VSTeamVersions]::MemberEntitlementManagement)
            
            $listurl += _appendQueryString -name "top" -value $top -retainZero
            $listurl += _appendQueryString -name "skip" -value $skip -retainZero
            $listurl += _appendQueryString -name "select" -value ($select -join ",")

            # Call the REST API
            $resp = _callAPI -url $listurl

            # Apply a Type Name so we can use custom format view and custom type extensions
            foreach ($item in $resp.value) {
                _applyTypesToUser -item $item
            }

            Write-Output $resp.value
        }
    } 
}

function Add-VSTeamUser {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [Alias('UserEmail')]     
        [string]$Email,
        [ValidateSet('Advanced', 'EarlyAdopter', 'Express', 'None', 'Professional', 'StakeHolder')]
        [string]$License = 'EarlyAdopter',
        [ValidateSet('Custom', 'ProjectAdministrator', 'ProjectContributor', 'ProjectReader', 'ProjectStakeholder')]
        [string]$Group = 'ProjectContributor'
    )
   
    DynamicParam {
        _buildProjectNameDynamicParam -Mandatory $false
    }

    process {
        # Thi swill throw if this account does not support MemberEntitlementManagement
        _supportsMemberEntitlementManagement

        # Bind the parameter to a friendly variable
        $ProjectName = $PSBoundParameters["ProjectName"]

        $obj = @{
            accessLevel         = @{
                accountLicenseType = $License
            }
            user                = @{
                principalName = $email
                subjectKind   = 'user'
            }
            projectEntitlements = @{
                group      = @{
                    groupType = $Group
                }
                projectRef = @{
                    id = $ProjectName
                }
            }
        }

        $body = $obj | ConvertTo-Json

        # Call the REST API
        _callAPI  -Method Post -Body $body -SubDomain 'vsaex' -Resource 'userentitlements' -Version $([VSTeamVersions]::MemberEntitlementManagement) -ContentType "application/json"
    }
}

function Update-VSTeamUser
{
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'ByEmail')]
   param (
      [Parameter(ParameterSetName = 'ById', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserId')]
      [string]$Id,

      [Parameter(ParameterSetName = 'ByEmail', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserEmail')]
      [string]$Email,

      [Parameter(Mandatory = $true)]
      [ValidateSet('Advanced', 'EarlyAdopter', 'Express', 'None', 'Professional', 'StakeHolder')]
      [string]$License,

      [switch]$Force
   )

   process {
      # This will throw if this account does not support MemberEntitlementManagement
      _supportsMemberEntitlementManagement

      if ($email)
      {
         # We have to go find the id
         $user = Get-VSTeamUser | Where-Object email -eq $email

         if (-not$user)
         {
            throw "Could not find user with an email equal to $email"
         }

         $id = $user.id
         $licenseOld = $user.accessLevel.accountLicenseType

      }
      else
      {
         $user = Get-VSTeamUser -Id $id
      }

      $obj = @{
         from = ""
         op = "replace"
         path = "/accessLevel"
         value = @{
            accountLicenseType = $License
            licensingSource = "account"
         }
      }

      $body = ConvertTo-Json -InputObject @($obj) 

      if ($Force -or $PSCmdlet.ShouldProcess("$( $user.userName ) ($( $user.email ))", "Update user"))
      {
         # Call the REST API
         _callAPI -Method Patch -Body $body -SubDomain 'vsaex' -Resource 'userentitlements' -Id $id -Version $([VSTeamVersions]::MemberEntitlementManagement) -ContentType 'application/json-patch+json' | Out-Null
      
         Write-Output "Updated user license for $( $user.userName ) ($( $user.email )) from ($licenseOld) to ($License)"
      }
   }
}

function Remove-VSTeamUser {
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'ById')]
    param(
        [Parameter(ParameterSetName = 'ById', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
        [Alias('UserId')]
        [string]$Id,

        [Parameter(ParameterSetName = 'ByEmail', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
        [Alias('UserEmail')]
        [string]$Email,

        [switch]$Force
    )

    process { 
        # Thi swill throw if this account does not support MemberEntitlementManagement
        _supportsMemberEntitlementManagement
        
        if ($email) {
            # We have to go find the id
            $user = Get-VSTeamUser | Where-Object email -eq $email

            if (-not $user) {
                throw "Could not find user with an email equal to $email"
            }

            $id = $user.id

        } else {
            $user = Get-VSTeamUser -Id $id
        }

        if ($Force -or $PSCmdlet.ShouldProcess("$($user.userName) ($($user.email))", "Delete user")) {
            # Call the REST API
            _callAPI -Method Delete -SubDomain 'vsaex' -Resource 'userentitlements' -Id $Id -Version $([VSTeamVersions]::MemberEntitlementManagement) | Out-Null

            Write-Output "Deleted user $($user.userName) ($($user.email))"
        }
    }
}

Set-Alias Get-User Get-VSTeamUser
Set-Alias Add-User Add-VSTeamUser
Set-Alias Update-User Update-VSTeamUser
Set-Alias Remove-User Remove-VSTeamUser

Export-ModuleMember `
    -Function Get-VSTeamUser, Add-VSTeamUser, Update-VSTeamUser, Remove-VSTeamUser `
    -Alias Get-User, Add-User, Update-User, Remove-User