Śledzenie licencjonowanych użytkowników Office (MSOL)

📦 Office 365 POWERSHELL ChrisTitusTech

Wyszukuje wszystkich użytkowników z przypisaną licencją na określony plan M365 i zapisuje wyniki do pliku CSV. Starsza wersja wykorzystująca moduł MSOnline (MSOL) — zastąpiona przez Track-M365LicensedUsers-Modern.ps1.

Pobierz .ps1

Opis

Finds all the MSOLUsers that are licensed with the specified plan and stores them in a csv. It populates an extra field in the csv the specifies the earliest point at which the csv knew the user was licensed (LicensedAsOf field). If a user doesn't show up in the licensed list at a later date the function takes note and populates another field in the csv with that date (DelicensedAsOf)

📄 Get-NewOfficeUsers.ps1 🕒 2026-04-13 📦 Źródło: christitustech
Get-NewOfficeUsers.ps1

Function Update-UserLicenseData {

<#
.SYNOPSIS
Finds all the MSOLUsers that are licensed with the specified plan and stores them in a csv

.DESCRIPTION
Finds all the MSOLUsers that are licensed with the specified plan and stores them in a csv.
It populates an extra field in the csv the specifies the earliest point at which the csv
knew the user was licensed (LicensedAsOf field). If a user doesn't show up in the licensed list at a later date
the function takes note and populates another field in the csv with that date (DelicensedAsOf)

.PARAMETER ServiceName
The Name of the Service Plan that you wish to track licensed users for

.PARAMETER CSVPath
The Full file path of the CSV you wish the data to be tracked in

.PARAMETER Credentials
Credentials for connecting to MSOL service

.PARAMETER Username
Used to generate Credentials for connecting to MSOL service

.PARAMETER Password
Used to generate Credentials for connecting to MSOL service

.Example
.pdate-UserLicenseData.ps1
Get list of Users that are licensed for OFFICESUBSCRIPTION service plan and store the results in an AppData Folder
The user will be prompted for their credentials.

.Example
.pdate-UserLicenseData.ps1 -ServiceName "OFFICESUBSCRIPTION"
Get list of Users that are licensed for OFFICESUBSCRIPTION service plan and store the results in public documents.
The user will be prompted for their credentials.

.Example
.pdate-UserLicenseData.ps1 -ServiceName "OFFICESUBSCRIPTION" -CSVPath "$env:Public\Documents\LicensedUsers.csv" -Credentials $creds
Get list of Users that are licensed for OFFICESUBSCRIPTION service plan and store the results in public documents.
The user won't be prompted for their credentials

.Notes
Proper use of this script should involve running this as a scheduled task

    1.  On the system that the task will be run from, open the Windows Task Scheduler. 
        This can be found in the Start menu, under Start > Administrative Tools.

    2.  In the Task Scheduler, select the Create Task option under the Actions heading 
        on the right-hand side.

    3.  Enter a name for the task, and give it a description (the description is optional 
        and not required).

    4.  In the General tab, go to the Security options heading and specify the user account 
        that the task should be run under. Change the settings so the task will run if the 
        user is logged in or not.

    5.  Next, select the Triggers tab, and click New to add a new trigger for the scheduled 
        task. This new task should use the On a schedule option. The start date can be set 
        to a desired time, and the frequency and duration of the task can be set based on 
        your specific needs. Click OK when your desired settings are entered.

    6.  Next, go to the Actions tab and click New to set the action for this task to run. 
        Set the Action to Start a program.

    7.  In the Program/script box enter "PowerShell."
        In the Add arguments (optional) box enter the value:

         .pdate-UserLicenseData.ps1 -ServiceName [ServiceName] -Username [username] -Password [password])

    8.  Then, in the Start in (optional) box, add the location of the folder that contains 
        your PowerShell script.

        Note: The location used in the Start in box will also be used for storing the scheduled task 
        run times, the job history for the copies, and any additional logging that may occur.
        Click OK when all the desired settings are made.

    9. Next, set any other desired settings in the Conditions and Settings tabs. You can also set up 
        additional actions, such as emailing an Administrator each time the script is run.

    10. Once all the desired actions have been made (or added), click OK. The task will be immediately 
        set, and is ready to run.

        The scheduling of this task is complete, and is now ready to run based on the entered settings.

#>

[CmdletBinding(DefaultParameterSetName="PSCredential")]
Param(

    [Parameter()]
    [string] $ServiceName = "OFFICESUBSCRIPTION",

    [Parameter()]
    [string] $CSVPath = "$env:APPDATA\Microsoft\OfficeAutomation\OfficeLicenseTracking.csv",

    [Parameter(ParameterSetName="PSCredential")]
    [PSCredential] $Credentials,

    [Parameter(ParameterSetName="UsernamePassword")]
    [string] $Username,

    [Parameter(ParameterSetName="UsernamePassword")]
    [string] $Password

)

Process{
    if($PSCmdlet.ParameterSetName -eq "UsernamePassword")
    {
        $PWord = ConvertTo-SecureString –String $Password –AsPlainText -Force
        $Credentials = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $Username, $PWord
    } else {
      if (!($Credentials)) {
        $Credentials = (Get-Credential)
      }
    }

    if ($Credentials) {
    $Domain = $Credentials.UserName.Split('@')[1]
    $CSVPath = "$env:APPDATA\Microsoft\OfficeAutomation\OfficeLicenseTracking-$Domain.csv"
    
    Write-host
    Write-host "Connecting to Office 365..."

    Connect-MsolService -Credential $Credentials

    Write-host "Retrieving User List..."

    $Users = Get-MsolUser | ? IsLicensed -eq $True | Select DisplayName, Licenses, LiveId, ObjectId, SignInName 
    
    #Get list of users with the correct service plan
    $LicensedUsers = new-object PSObject[] 1;
    foreach($User in $Users){
        :LicenseLoop foreach($License in $User.Licenses){
            foreach($ServiceStatus in $License.ServiceStatus){
                if($ServiceStatus.ServicePlan.ServiceName -eq $ServiceName){
                    $LicensedUsers += $User
                    break LicenseLoop
                }
            }
        }
    }
        
    #Add tracking properties
    foreach($User in $LicensedUsers){
        if($User -ne $Null){
            Add-Member -InputObject $User -MemberType NoteProperty -Name LicensedAsOf -Value "$(Get-Date -Format "yyyy-MM-dd hh:mm")"
            Add-Member -InputObject $User -MemberType NoteProperty -Name DelicensedAsOf -Value "-"
        }
    }

    $pathSplit = Split-Path -Path $CSVPath
    $createDir = [system.io.directory]::CreateDirectory($pathSplit)

    #Check if CSV exists
    if(Test-Path $CSVPath){
        #if CSV exists, import it and compare and update values
        $ImportedCSV = Import-Csv $CSVPath
        

        Foreach($ImportedUser in $ImportedCSV){
            $CheckUser = $LicensedUsers | ? ObjectId -eq $ImportedUser.ObjectId
            if($CheckUser -eq $Null){
                $ImportedUser.DelicensedAsOf = "$(Get-Date -Format "yyyy-MM-dd hh:mm")"
            }
        }

        Foreach($LicensedUser in $LicensedUsers){
            $CheckUser = $ImportedCSV | ? ObjectId -eq $LicensedUser.ObjectId
            if($CheckUser -eq $Null){
                if($LicensedUser -ne $Null){
                    $ImportedCSV += $LicensedUser
                }
            }
        }
        $ImportedCSV | Export-Csv $CSVPath -NoTypeInformation

        Write-host "CSV File Updated: $CSVPath"
    }else{
        #If csv does not exist, export data
        $LicensedUsers | ? ObjectId -ne $Null | Export-Csv $CSVPath -NoTypeInformation

        Write-host "CSV File Created: $CSVPath"
    }
    }
}

}

Function Get-RecentlyLicensedUsers {

<#
.SYNOPSIS
Get a list of users the were licensed after the specified date according to the specified csv

.DESCRIPTION
Get a list of users the were licensed after the specified date according to the specified csv.
It is important to have run the Update-UserLicenseData.ps1 prior to using this script.

.PARAMETER CutOffDate
The cutoff date for how new you wish the return list of users to be

.PARAMETER CSVPath
The Full file path of the CSV with the data (should be the same as the path used for
Update-UserLicenseData.ps1).

.Example
Get-RecentlyLicensedUsers 
Get list of Users that are were licensed in the last 7 days if the Update-UserLicenseData cmdlet has already been run

.Example
Get-RecentlyLicensedUsers -CutOffDate (Get-Date "2015-7-13) -CSVPath "$env:Public\Documents\LicensedUsers.csv"
Get list of Users that are were licensed after July 7, 2015 according to specified csv

#>

[CmdletBinding()]
Param(

    [Parameter()]
    [string] $CSVPath,

    [Parameter()]
    [DateTime] $CutOffDate

)

Begin {
    $defaultDisplaySet = 'DisplayName', 'SignInName'

    $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’,[string[]]$defaultDisplaySet)
    $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
}

Process{
    
    if (!($CutOffDate)) {
       $CutOffDate = (Get-Date).AddDays(-7)
    }

    [System.IO.FileSystemInfo[]]$filePaths = @()

    if (!($CSVPath)) {
        $childItems = Get-ChildItem -Path "$env:APPDATA\Microsoft\OfficeAutomation" | where {$_.Extension.ToLower() -eq ".csv" }
        foreach ($csvFile in $childItems) {
           $filePaths += $csvFile
        }
    } else {
       $filePaths += ([System.IO.FileInfo]"$CSVPath")
    }
    
    if ($filePaths.Length -eq 0) {
      Write-Host "No CSV File Exits. Please run Update-UserLicenseData to generate the CSV File."
    }

    foreach ($csvFile in $filePaths) {
        $fileName = $csvFile.Name.Replace($csvFile.Extension, "")
        $domain = ""
        if ($fileName.Contains("-")) {
           $domain = $fileName.Split('-')[1]
        }
       
        if (($PSCmdlet.MyInvocation.PipelineLength -eq 1) -or `
            ($PSCmdlet.MyInvocation.PipelineLength -eq $PSCmdlet.MyInvocation.PipelinePosition)) {

            Write-Host ""
            Write-Host "Retrieving New Users Since: $CutOffDate"
            Write-Host ""
            if ($domain) {
               Write-Host "Domain: $domain"
            }
        }

        $NewUsers = new-object PSObject[] 1;

        $ImportedCSV = Import-Csv -LiteralPath $csvFile.FullName

        foreach($User in $ImportedCSV){
            if ($CutOffDate -lt (Get-Date($user.LicensedAsOf))) {
              if ($domain) {
                Add-Member -InputObject $User -MemberType NoteProperty -Name "Domain" -Value $domain
              }
              $User | Add-Member MemberSet PSStandardMembers $PSStandardMembers

              $NewUsers += $User
            }
        }

        return $NewUsers
    }
}

}