PowerShell collection exported to separate row in CSV - powershell

I have the following script that will export all the Office 365 licenses per user that is enabled for a license to a CSV. The export will list each user and then the multiple licenses on the same row like this:
There is not a set number of licenses for each user, users will have different licenses.
Instead of having them export on the same row, I need to have the license for each user on a separate row like this:
How can I modify the script to make that happen?
#### All lincesed users###
$JobCollection = #()
$mbx = Get-MsolUser -all | Where-Object { $_.isLicensed -eq "TRUE" }
$count=0
$fullcount = ($mbx | measure-object).count
foreach ($M in $MBX) {
$count++
$search = $m.userprincipalname.tostring()
Write-progress -activity "Getting Calendar $count out of $fullcount --- $search " -percentcomplete (($count / $fullcount)*100) -status "Processing"
$License = (((Get-MsolUser -UserPrincipalName $M.userprincipalname).Licenses.accountskuid) -join ",")
#$queuelink = $job.reportingqueueuri.absoluteuri
#$jobID = $Job.jobid
$Jobs = New-Object psobject
$JObs | Add-Member -MemberType NoteProperty -Name UserPrincipalName -Value $M.userprincipalname
$JObs | Add-Member -MemberType NoteProperty -Name License -Value $license
$JobCollection += $jobs
}
$JobCollection | export-csv "C:\test\AllMBXlicensse.csv" -NoTypeInformation

You can create a nested loop to assign each value to it's corresponding userprincipalname in a PSCustomObject; like you're already doing:
$mbx = Get-MsolUser -all | Where-Object { $_.isLicensed -eq $true }
$mbxCount = $mbx.count
for ($i = 0; $i -lt $mbx.count; $i++)
{
$UPN = $mbx[$i].userprincipalname
$progressSplat = #{
Activity = "Getting Calendar $i out of $mbx --- $UPN"
PercentComplete = (($i / $mbxCount)*100)
Status = "Processing"
}
Write-Progress #progressSplat
$userLicenses = (Get-MsolUser -UserPrincipalName $UPN).Licenses.accountskuid
foreach ($license in $userLicenses)
{
[PSCustomObject]#{
UserPrincipalName = $UPN
License = $license
} | Export-Csv -Path "C:\test\AllMBXlicensse.csv" -Append -Force -NoTypeInformation
}
}
...or, as TheMadTechnician proposed, using a calculated property (very elegant might I add):
$mbx = Get-MsolUser -all | Where-Object { $_.isLicensed -eq $true }
$mbxCount = $mbx.count
for ($i = 0; $i -lt $mbx.count; $i++)
{
$UPN = $mbx[$i].userprincipalname
$progressSplat = #{
Activity = "Getting Calendar $i out of $mbxCount --- $UPN"
PercentComplete = (($i / $mbxCount)*100)
Status = "Processing"
}
Write-Progress #progressSplat
(Get-MsolUser -UserPrincipalName $UPN).Licenses.accountskuid |
Select-Object -Property #{
Name = "UserPrincipalName"
Expression = { $UPN }
}, #{
Name = "License"
Expression = { $_ }
} | Export-Csv -Path "C:\test\AllMBXlicensse.csv" -Append -Force -NoTypeInformation
}

Related

Not able to add data to csv file powershell

I am trying to add data to an csv file.
I am creating the csv with header first and then trying to add the rows. but it is returning blank csv file
$props=[ordered]#{
ServerName=''
SystemFolderPath=''
IdenityReference=''
FileSystemRights=''
}
New-Object PsObject -Property $props |
Export-Csv "C:\status_report.csv" -NoTypeInformation
$serverlist = Get-Content -Path "C:\ServerList.txt"
foreach($server in $serverlist)
{
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list)
{
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object {$_.IdentityReference}
#$Users_Permission
Foreach ($user in $Users_Permissions)
{
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
$NewLine = "{0},{1},{2},{3}" -f $server,$sys_Path,$IdenityReference,$FileSystemRights
$NewLine | Export-Csv -Path "C:\status_report.csv" -Append -NoTypeInformation -Force
}
}
}
Please let me know what I am doing wrong here
The main reason why you're seeing this is because Export-Csv expects an object or object[] through the pipeline and you're passing a formatted string instead. This is specified on MS Docs:
Do not format objects before sending them to the Export-CSV cmdlet. If Export-CSV receives formatted objects the CSV file contains the format properties rather than the object properties.
PS /> 'server01,C:\Windows,Computer\User,FullControl' | ConvertTo-Csv
"Length"
"45"
Instead of appending to a CSV which is quite inefficient, unless there is a specific need for this, what you will want to do is collect the results first and then export them.
I'm not too sure why | Where-Object { $_.IdentityReference } is needed, I left it there but I don't think it's needed.
Regarding $serverlist, if you will run this on remote hosts you would be better of using Invoke-Command since it allows parallel invocations. The outer loop wouldn't be needed in that case:
$serverlist = Get-Content -Path "C:\ServerList.txt"
# Collect results here
$result = Invoke-Command -ComputerName $serverlist -ScriptBlock {
$paths_list = $env:Path -Split [System.IO.Path]::PathSeparator
foreach($sys_Path in $paths_list)
{
$Permissions = (Get-Acl -Path $sys_Path).Access
foreach($acl in $Permissions)
{
if(-not $acl.IdentityReference)
{
continue
}
[pscustomobject]#{
ComputerName = $env:ComputerName
SystemFolderPath = $sys_Path
IdenityReference = $acl.IdentityReference.Value
FileSystemRights = $acl.FileSystemRights
}
}
}
} -HideComputerName
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation
Accept Santiago above but this is what I did with what you wrote.
$props = [ordered]#{
ServerName = ''
SystemFolderPath = ''
IdenityReference = ''
FileSystemRights = ''
}
New-Object PsObject -Property $props |
Export-Csv "C:\status_report.csv" -NoTypeInformation
$serverlist = Get-Content -Path "C:\ServerList.txt"
$result = $serverlist | ForEach-Object {
foreach ($server in $_) {
$paths_list = $null
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list) {
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object { $_.IdentityReference }
#$Users_Permission
Foreach ($user in $Users_Permissions) {
$IdenityReference = $null
$FileSystemRights = $null
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
[PSCustomObject]#{
Server = $server
Sys_Path = $sys_Path
Referecent = $IdenityReference
Rights = $FileSystemRights
}
$sys_Path = $null
}
}
}
}
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation
Santiago's answer is correct and contains all the required information for you to understand the issue you have here.
I just wanted to provide you with the minimum modifications to be done in your script:
Replace the $props custom object by a function (i.e CreateCustomObject)
function CreateCustomObject($val1, $val2, $val3, $val4) {
$NewObject = New-Object PSObject ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "ServerName" -Value $val1 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "SystemFolderPath" -Value $val2 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "IdenityReference" -Value $val3 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "FileSystemRights" -Value $val4 ;
return $NewObject ;
}
Replace the String Variable $NewLine by an Array
$NewLine = #()
$NewLine += CreateCustomObject $server $sys_Path $IdenityReference $FileSystemRights
Write to CSV only once data is collected (move the command to the end of the script)
So the final script will look something like that:
function CreateCustomObject($val1, $val2, $val3, $val4) {
$NewObject = New-Object PSObject ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "ServerName" -Value $val1 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "SystemFolderPath" -Value $val2 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "IdenityReference" -Value $val3 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "FileSystemRights" -Value $val4 ;
return $NewObject ;
}
$serverlist = Get-Content -Path "C:\Temp\ServerList.txt"
$NewLine = #()
foreach($server in $serverlist) {
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list) {
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object {$_.IdentityReference}
#$Users_Permission
Foreach ($user in $Users_Permissions) {
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
$NewLine += CreateCustomObject $server $sys_Path $IdenityReference $FileSystemRights
}
}
}
$NewLine | Export-Csv -Path "C:\temp\status_report.csv" -NoTypeInformation -Force

Trying to get all Teams with their owners, members and guest in a CSV using Powershell

I'm working on a script that allows me to fetch all of our Teams groups by their IDs and list the Id, Name, Owner(s), Member(s) and Guest(s).
The code works to a certain point, I get all the needed information, but it seems to be limiting it to 2 owners, 4 members and no guests...
When I run the code with adding it to a PSObject and simply do a write-host all the data is there, but I can't append it correctly to a CSV.
Code below, its either a limitation of the PSObject or I am doing something wrong/ missing something (hoping on the 2nd part ;) )
try
{
$host.Runspace.ThreadOptions = "ReuseThread"
# Get the credentials
Connect-AzureAD
# Connect to Microsoft Teams
Connect-MicrosoftTeams
# Get all the teams from tenant
[array]$teamColl = $null
[array]$ownerColl = $null
[array]$memberColl = $null
[array]$guestColl = $null
$teamColl=Get-Team
$date = Get-Date -Format "yyyy-MM-dd"
$OutputFile01 = "C:\temp\GetTeamsOwnersAndMembers-$date.csv"
# Clean file
Remove-Item $OutputFile01 -ErrorAction SilentlyContinue
$objectCollection=#()
$ownerCount = 0
$memberCount = 0
$guestCount = 0
# Loop through the teams
foreach($team in $teamColl)
{
$object = New-Object PSObject
# Get the Teams basic information
$object | Add-Member -type NoteProperty -Name ID -Value $team.GroupId
$object | Add-Member -type NoteProperty -Name TeamsName -Value $team.DisplayName
#$object | Add-Member -type NoteProperty -Name Description -Value $team.Description
# Get the Teams owners
$ownerColl = Get-TeamUser -GroupId $team.GroupId -Role Owner
$memberColl = Get-TeamUser -GroupId $team.GroupId -Role Member
$guestColl = Get-TeamUser -GroupId $team.GroupId -Role Guest
#Write-Host "$ownerColl"
#Write-Host "$memberColl"
#Write-Host "$guestColl"
# Loop through the owners
foreach($owner in $ownerColl)
{
$ownerCount++
$object | Add-Member -type NoteProperty -Name Owner_$ownerCount -Value $owner.User
}
# Loop through the members
foreach($member in $memberColl)
{
$memberCount++
$object | Add-Member -type NoteProperty -Name Member_$memberCount -Value $member.User
}
# Loop through the guests
foreach($guest in $guestColl)
{
$guestCount++
$object | Add-Member -type NoteProperty -Name Guest_$guestCount -Value $guest.User
}
# Reset counters
$ownerCount = 0
$memberCount = 0
$guestCount = 0
$objectCollection += $object
}
$objectCollection | Export-Csv $OutputFile01 -NoTypeInformation
}
catch [System.Exception]
{
Write-Host -ForegroundColor Red $_.Exception.ToString()
}
finally
{
Write-Host "Done"
}
Was able to solve it, I needed to use the -join to add the additional users :)
Working code:
try
{
$host.Runspace.ThreadOptions = "ReuseThread"
# Get the credentials
Connect-AzureAD
# Connect to Microsoft Teams
Connect-MicrosoftTeams
# Get all the teams from tenant
[array]$teamColl = $null
[array]$ownerColl = $null
[array]$memberColl = $null
[array]$guestColl = $null
$teamColl=Get-Team
$date = Get-Date -Format "yyyy-MM-dd"
$OutputFile01 = "C:\temp\GetTeamsOwnersAndMembers-$date.csv"
# Clean file
Remove-Item $OutputFile01 -ErrorAction SilentlyContinue
$GroupsCSV=#()
Write-Host -ForegroundColor Green "Processing Groups"
# Loop through the teams
foreach($team in $teamColl)
{
$ownerCount = 0
$memberCount = 0
$guestCount = 0
Write-Host -ForegroundColor Yellow -NoNewline "."
$ownerColl = Get-TeamUser -GroupId $team.GroupId -Role Owner
$ownerCollection=#()
# Loop through the owners
foreach($owner in $ownerColl)
{
$ownerCount++
$ownerCollection += $owner.User
}
$memberColl = Get-TeamUser -GroupId $team.GroupId -Role Member
$memberCollection=#()
# Loop through the members
foreach($member in $memberColl)
{
$memberCount++
$memberCollection += $member.User
}
$guestColl = Get-TeamUser -GroupId $team.GroupId -Role Guest
$guestCollection=#()
# Loop through the guests
foreach($guest in $guestColl)
{
$guestCount++
$guestCollection += $guest.User
}
# Create CSV file line
$GroupsRow = [pscustomobject]#{
GroupId = $team.GroupId
Name = $team.DisplayName
OwnerCount = $ownerCount
MemberCount = $memberCount
GuestCount = $guestCount
Owners = $ownerCollection -join " | "
Members = $memberCollection -join " | "
Guests = $guestCollection -join " | "
}
# Add to export array
$GroupsCSV+=$GroupsRow
}
# Export to CSV
Write-Host -ForegroundColor Green "`nCreating and exporting CSV file"
$GroupsCSV | Export-Csv -NoTypeInformation -Path $OutputFile01
}
catch [System.Exception]
{
Write-Host -ForegroundColor Red $_.Exception.ToString()
}
finally
{
Write-Host "Done"
}

Powershell querying users in Active Directory

I am having a hard time figuring out a more efficient way of querying info from AD. As it stands I import a .csv file of active users from our student information system. Then I want to create a new .csv file of active users info from AD. As such, I am querying AD on every user (approx 10k students.) I have a feeling I could somehow accomplish this with one query, but no luck. The students match on a numeric ID that is stored in the AD title field. The code does work, however it takes hours to run. Here is what I use:
$Users = Import-Csv "c:\DASLExport.csv" -Header #("a") | Select a
$usersarray = #()
ForEach ($Row in $Users) {
$userSearchString = $Row.a
$currentUser = (Get-ADUser -Filter {Title -eq $userSearchString} -Properties title, SamAccountName, extensionAttribute1)
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
$usersarray += $UserObj
}
If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}
I think, instead of query each user with Get-ADUser , Get all users with title at once and save it to a variable, Then query this variable instead.
Also, Regular Arrays are in fixed size, which mean that each time you insert new element you actually create new array and copy all the data into it, and you repeat it again and again, which take much time. so switch to ArrayList which is intend to grow, it will be much faster.
Check it yourself:
$ArrayList = New-Object System.Collections.ArrayList
$RegularArray = #()
Measure-Command { 1..10000 | % {[void]$ArrayList.Add($_)} }
Measure-Command { 1..10000 | % {$RegularArray += $_ } }
So For example try this:
$Users = Import-Csv "c:\DASLExport.csv" -Header #("a") | Select a
$ADUsers = Get-ADUser -Filter {Title -ne "$null"} -Properties title, SamAccountName, extensionAttribute1
$Usersarray = New-Object System.Collections.ArrayList
ForEach ($Row in $Users) {
$userSearchString = $Row.a
$currentUser = $ADUsers | ? {$_.Title -eq $userSearchString}
if (!$currentUser) {continue}
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
[void]$usersarray.Add($UserObj)
}
If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}
While #Avshalom's answer is useful, it can be improved:
[CmdletBinding()]
param
(
[Parameter(Position = 0)]
[ValidateScript({Test-Path -Path $PSItem -PathType Leaf})]
[string]
$Path = 'C:\DASLExport.csv',
[Parameter(Position = 1)]
[ValidateScript({Test-Path -Path $PSItem -PathType Leaf -IsValid})]
[string]
$Destination = 'C:\users.csv'
)
$csv = Import-Csv -Path $Path -Header a
$users = #(Get-ADUser -Filter 'Title -ne "$null"' -Properties Title, SamAccountName, extensionAttribute1)
$collection = foreach ($row in $csv)
{
$title = $row.a
$user = $users.Where{$PSItem.Title -eq $title}
if (-not $user)
{
Write-Warning -Message "User $title not found."
continue
}
[pscustomobject]#{
ID = $user.Title
Username = $user.SamAccountName
Password = $user.extensionAttribute1
}
}
$collection | Export-Csv -Path $Destination -NoTypeInformation
You can assign the output of the foreach loop to a variable directly, avoiding the need to manage a list object (although if you do opt for a list, you should use System.Collections.Generic.List<Type> since ArrayList is deprecated). Additionally, you don't need to use a Select-Object statement since your csv was already loaded and it just processes it twice in that scenario. The biggest speed improvement is not querying AD thousands of times, keeping it in a single object, but MOSTLY by not using [array]/#().
Speed comparisons:
$L = 1..100000
Measure-Command {$col = foreach ($i in $L) { $i }}
~70ms
Measure-Command {$col = [System.Collections.Generic.List[int]]::new(); foreach ($i in $L) { $col.Add($i) }}
~110ms
Measure-Command {$col = #(); foreach ($i in $L) { $col += $i }}
~46 SECONDS

How to use PowerShell to find information for Current User's Network Printers

I'm trying to find the mapped network printers of the signed in user. I found this script here: GetMappedNetworkPrinters, but it is from a few years ago and there isn't a comment section so I'm not sure where to ask questions about the issues...
When I run this script I run into WMI errors on some machines, but the confusing part is for the computers that it does bring information back for I get the notice that there are no mapped printers...
Any idea how to adjust the script so that it might work correctly?
<#
Script Name: GetMappedNetworkPrinters.ps1
Purpose:
This script can be used to collect the mapped network printer information from the users who are logged into the console of the Computer or Computers specified.
Required Modules:
PSRemoteRegistry, and Active Directory
Permission Requirements:
The user account that the script is run with needs to have administrative permissions on the workstations and permission to query Active Directory user accounts.
The computers firewall if enabled needs to allow it to be pinged, connections to WMI and also Remote Registry.
A user will need to be logged into the console so their mapped network printer information can be collected.
How the script functions:
Create a text file that contains a list of Computer names that you want to get the mapped network printers info for.
Execute the script and you will be prompted for the path to the text file that contains the list.
Connectivity will be verified to each of the computers by pinging each of them.
Via WMI it will check to see which user is logged into the computers that responded to the ping.
Next it will query Active Directory for the SID of each of the users that were currently logged into one of the active computers polled.
Using the users SID a Remote Registry query is created to enumerate the list of mapped network printers for the logged on user.
The Log files and CSV file containing the list of mapped printers is located in C:\temp\logs
FileNames:
MappedPrinters-(currentdate).csv -- Contains the list of mapped printers.
NoMappedPrinters-(currentdate).log -- Contains list of users that do not have network printers mapped on their computer.
NoReply-(currentdate).csv -- Contains list of computers that did not respond to ping.
NoUsrLoggedIn-(currentdate).log -- Contains list of computers that responded to ping but did not have a user logged into it.
RemoteRegNotRunning-(currentdate).log -- Contains a list of computers where the Remote Registry service is not running.
WmiError-(currentdate).log -- If there are computers that it is not able to connect to via wmi it will be listed here.
#>
function global:Ping-Host {
BEGIN {
}
PROCESS {
$results = gwmi -Query "SELECT * FROM Win32_PingStatus WHERE Address = '$_'"
$obj2 = New-Object psobject
$obj2 | Add-Member Noteproperty Computer $_
$obj2 | Add-Member Noteproperty IPAddress ($results.protocoladdress)
if ($results.statuscode -eq 0) {
$obj2 | Add-Member NoteProperty Responding $True
} else {
$obj2 | Add-Member NoteProperty Responding $False
}
Write-Output $obj2
}
END {}
}
function VerifyConnectivity {
param (
[parameter(ValueFromPipeline=$true)]
$compList
)
BEGIN {
$modeMSG = "Verifying Connectivity to Desktops"
$HostComputer = #()
$d = Get-Date
$strDate = $d.ToString()
$month = $d.Month
$day = $d.Day
$year = $d.Year
$cDate = "$month-$day-$year"
$logFilePath = "C:\temp\logs\"
$NoReplyLog = $logFilePath + "NoReply-" + $cDate + ".csv"
}
PROCESS {
$i = 1
$numComp = $compList.Count
If ($numComp -ge 1){
Talk $modeMSG
$HostComputer = $HostComputer + $(
foreach ($computer in $compList){
Write-Progress -Activity $modeMSG -Status "Currently Processing: $computer" -CurrentOperation "$i of $numComp" -PercentComplete ($i/$numComp*100)
$computer | Ping-Host
$i = $i + 1
})
}
ElseIf ($numComp -lt 1){
Write-Host "No Computers to Process"
Exit
}
}
END {
$Alive = $HostComputer | Where {$_.Responding -eq "$true"}
$global:Dead = $HostComputer | Where {$_.Responding -ne "$true"}
$global:Dead | select Computer | Export-Csv -Path $NoReplyLog
$Acomp = $Alive | select Computer
$Acomp
}
}
function GetPrinterInfo {
param (
[parameter(ValueFromPipeline=$true)]
$compList
)
BEGIN {
$d = Get-Date
$strDate = $d.ToString()
$month = $d.Month
$day = $d.Day
$year = $d.Year
$cDate = "$month-$day-$year"
$global:logFilePath = "C:\temp\logs\"
$NoPrtMapLog = $logFilePath + "NoMappedPrinters-" + $cDate + ".log"
$WmiErrorLog = $logFilePath + "WmiError-" + $cDate + ".log"
$MappedPrinters = $logFilePath + "MappedPrinters-" + $cDate + ".csv"
$NoUsrLoggedIn = $logFilePath + "NoUsrLoggedIn-" + $cDate + ".log"
$RemoteRegNotRunning = $logFilePath + "RemoteRegNotRunning-" + $cDate + ".log"
$ErrorActionPreference = 'SilentlyContinue'
Import-Module activedirectory
Import-Module psremoteregistry
$global:wmiErrors = #()
$global:NoUserLoggedIn = #()
$CompUserInfo = #()
$arrCompLogonInfo = #()
$arrRemoteRegSvcStopped = #()
$arrNoMappedPrinters = #()
$arrMappedPrinters = #()
$statusMSG = "Getting Logged on User Information"
$statusMSG2 = "Getting User SID from Active Directory"
$statusMSG3 = "Collecting Mapped Printer Information"
}
PROCESS {
$u = 1
$Responded = VerifyConnectivity $compList
if ($Responded.count -gt 0){
Talk $statusMSG
foreach ($client in $Responded){
[string]$c = $client.Computer
$numClient = $Responded.Count
$logonInfo = $null
Write-Progress -Activity $statusMSG -Status "Currently Processing: $c" -CurrentOperation "$u of $numClient" -PercentComplete ($u/$numClient*100)
$logonInfo = Get-WmiObject -ComputerName $c -Query "select * from win32_computersystem" | select Username
if ($?){
if ($logonInfo.Username -ne $null){
[string]$strUserName = $logonInfo.Username
$arrStrUserName = $strUserName.Split("\")
$strUser = $arrStrUserName[1]
$objCUinfo = New-Object psobject
$objCUinfo | Add-Member NoteProperty Workstation $c
$objCUinfo | Add-Member NoteProperty User $strUser
$CompUserInfo = $CompUserInfo + $objCUinfo
}
elseif ($logonInfo.Username -eq $null){
$global:NoUserLoggedIn = $global:NoUserLoggedIn + $c
}
}
else {
$global:wmiErrors = $global:wmiErrors + "Could not Execute WMI Query to collect user logon information on $c"
}
$u = $u + 1
}
if ($CompUserInfo.Count -ge 1){
$u = 1
Talk $statusMSG2
foreach ($logon in $CompUserInfo){
[string]$userLN = $logon.User
$userCount = $CompUserInfo.count
[string]$wrksta = $logon.Workstation
Write-Progress -Activity $statusMSG2 -Status "Currently Processing: $userLN" -CurrentOperation "$u of $userCount" -PercentComplete ($u/$userCount*100)
$getSID = Get-ADUser -Identity $userLN | select SID
if ($?){
[string]$sid = $getSID.sid
$LoggedOnUserInfo = New-Object psobject
$LoggedOnUserInfo | Add-Member Noteproperty Workstation $wrksta
$LoggedOnUserInfo | Add-Member Noteproperty User $userLN
$LoggedOnUserInfo | Add-Member Noteproperty SID $sid
$arrCompLogonInfo = $arrCompLogonInfo + $LoggedOnUserInfo
}
$u = $u + 1
}
}
if ($arrCompLogonInfo.count -ge 1){
$u = 1
Talk $statusMSG3
foreach ($comp in $arrCompLogonInfo){
$numT = $arrCompLogonInfo.Count
$Printers = $null
[string]$cn = $comp.Workstation
[string]$usid = $comp.sid
[string]$uName = $comp.User
Write-Progress -Activity $statusMSG3 -Status "Currently Processing: $cn" -CurrentOperation "$u of $numT" -PercentComplete ($u/$userCount*100)
$regStat = Get-Service -ComputerName $cn -Name "RemoteRegistry"
If ($?){
If ($regStat.Status -eq "Running"){
$Printers = Get-RegKey -ComputerName $cn -Hive "Users" -Key "$usid\Printers\Connections" -Recurse
If ($Printers -ne $null){
foreach ($printer in $Printers){
[string]$printerKey = $printer.key
$arrPrinterKey = $printerKey.Split("\")
$PrinterNamePiece = $arrPrinterKey[3]
$arrPrinterParts = $PrinterNamePiece.Split(",")
$printServer = $arrPrinterParts[2]
$PrinterName = $arrPrinterParts[3]
$PrinterUnc = "\\$printServer\$PrinterName"
$printInfo = New-Object psobject
$printInfo | Add-Member NoteProperty Workstation $cn
$printInfo | Add-Member NoteProperty User $uName
$printInfo | Add-Member NoteProperty PrintServer $printServer
$printInfo | Add-Member NoteProperty PrinterName $PrinterName
$printInfo | Add-Member NoteProperty PrinterUNC $PrinterUnc
$arrMappedPrinters = $arrMappedPrinters + $printInfo
}
}
ElseIf ($Printers -eq $null){
$arrNoMappedPrinters = $arrNoMappedPrinters + "$uName has no mapped printers on $cn"
}
}
ElseIf ($regStat.Status -eq "Stopped"){
$arrRemoteRegSvcStopped = $arrRemoteRegSvcStopped + $cn
}
}
$u = $u + 1
}
}
}
}
END {
$arrMappedPrinters | Export-Csv -Path $MappedPrinters
Add-Content $NoPrtMapLog $arrNoMappedPrinters
Add-Content $WmiErrorLog $wmiErrors
Add-Content $NoUsrLoggedIn $global:NoUserLoggedIn
Add-Content $RemoteRegNotRunning $arrRemoteRegSvcStopped
}
}
function Talk {
param (
[parameter(ValueFromPipeline=$true)]
$talk
)
Add-Type -AssemblyName System.Speech
$synthesizer = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
$synthesizer.Speak($talk)
}
cls
$getPath = $(Read-Host "Enter path to the text file that contains the list of Computer Names`n")
cls
if ($getPath -like "*.txt"){
$valid = Test-Path -Path $getPath
if ($valid -eq $true){
$compList = get-content -Path $getPath
GetPrinterInfo $compList
Write-Host "The Script Output is located in $logfilepath"
Exit
}
Else {
Write-Host "Path to file is not valid" -ForegroundColor Red
}
}
Elseif ($getPath -notlike "*.txt"){
Write-Host "Path to file is not valid"
Exit
}

O365 PowerShell Pull SubLicense

I am trying to pull a list of all users in my O365 Tenant and if they are licensed, the list of sublicenses they have been granted. The following code works great to list out my sublicenses:
$userlicensetest = get-msoluser -userprincipalname "steve.dorr#merrillcorp.com"
$userlicensetest.licenses[0].servicestatus
ServicePlan :: ProvisioningStatus
----------- :: ------------------
INTUNE_O365 :: PendingActivation
YAMMER_ENTERPRISE :: PendingInput
OFFICESUBSCRIPTION :: Success
So I tried to modify code I found online to include the sublicense information. Here is what I have built so far:
$ReportPath = "c:\users\userlist.csv"
Add-Content -value ("UserPrincipalName"+","+"IsLicensed"+","+ "Licenses”"+","+ "SubLicenses") -Path $ReportPath
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
$UserPrincipalName = $User.UserPrincipalName
$IsLicensed = $User.IsLicensed
$Licenses = $User.Licenses.AccountSkuId
$SubLicenses = $User.Licenses[0].servicestatus
Add-Content -value ($UserPrincipalName+","+$IsLicensed+","+$Licenses+","+$SubLicenses) -Path $ReportPath
}
The problem is it is only pulling the header line from the sublicense query and not all the lines of detail. So the line for myself in the CSV looks like:
Steve.Dorr#MerrillCorp.com TRUE mymerrillcorp:ENTERPRISEPACK Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus
Which does not give me the detail lines I needed.
How do I pull all the lines that Licenses[0].servicestatus generates into the CSV file? I don't care whether it flattens it out and goes across more columns, or takes up multiple lines in Excel.
Thanks.
So since I posted this question I have worked a little on it. I do not have a perfect solution that puts this into a nice neat CSV file, but I do have a routine which now drops all this information into a text file. Below is my code.
$MyCredentials = Get-Credential -Message "Enter Office 365 Email & Password"
Connect-MsolService -Credential $MyCredentials
$ReportFile = "C:\temp\O365Data.txt"
" " | Out-File $ReportFile #erases the file if it exists
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
$UserPrincipalName = $User.UserPrincipalName
$IsLicensed = $User.IsLicensed
$Licenses = $User.Licenses.AccountSkuId
$SubLicenses = $User.Licenses[0].servicestatus
$OneLine = $UserPrincipalName + " " + $IsLicensed
$OneLine| Out-File $ReportFile -Append
if($User.Licenses[0].servicestatus) {$User.Licenses[0].servicestatus | Out-File $ReportFile -Append}
}
To create the report you'll need to create custom objects to hold the properties you are interested in.
The following will take into account all the different licenses that could be applied to a user and then generate a csv with one user listed per line.
# Connect to o365
$MyCredentials = Get-Credential -Message "Enter Office 365 Email & Password"
Connect-MsolService -Credential $MyCredentials
# Prepare result file
$ExportFile = ".\o365Output.csv"
Remove-Item $ExportFile
$Result = #()
# Query all Msol Users
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
# Generate a new object for each user
$ReturnObject = [pscustomobject]#{
UserPrincipalName = $User.UserPrincipalName
IsLicensed = $User.IsLicensed
Licenses = [string]$User.Licenses.AccountSkuId
}
# In the event multiple licenses are found append properties for each license
foreach ($License in $User.Licenses)
{
if($($License.ServiceStatus.ServicePlan.ServiceName).count -eq 1)
{
$ReturnObject | Add-Member -MemberType NoteProperty -Name $License.ServiceStatus.ServicePlan.ServiceName -Value $License.ServiceStatus.ProvisioningStatus
}
else
{
for($i = 0; $i -lt $($License.ServiceStatus.ServicePlan.ServiceName).count; $i++)
{
$ReturnObject | Add-Member -MemberType NoteProperty -Name $License.ServiceStatus.ServicePlan.ServiceName[$i] -Value $License.ServiceStatus.ProvisioningStatus[$i]
}
}
}
$Result += $ReturnObject
}
# Combine properties from all returned objects
$Properties = $Result | ForEach-Object { Get-Member -InputObject $_ -MemberType NoteProperty | Select-Object -ExpandProperty Name } | Select-Object -Unique | Sort-Object
$Headers = #("UserPrincipalName")
$Headers += $Properties -notlike "UserPrincipalName"
# Export to csv
$Result | Select-Object $Headers | Export-Csv -NoTypeInformation $ExportFile
# Open csv
Invoke-Item $ExportFile