Having issues with if/else I end up with two output files - powershell

Having issues with if/else I end up with two output files, I just need
C:\$UserName-$ComputerName-OpenPSTs-$Date.csv if a PST exists and to include details.
or C:\$UserName-$ComputerName-NOPSTs-$Date.csv if no PST exist with "No PSTs Found" written in the file.
Any help would be gratefully appreciated.
#---------------------------------h
#Getting date and system variables
#---------------------------------
$Date = Get-Date -format d-M-yyyy
$UserName = $env:USERNAME
$ComputerName = $env:COMPUTERNAME
#-------------------------------
#Launch Outlook and check stores
#-------------------------------
$Outlook = New-Object -comObject Outlook.Application
$Object = $Outlook.Session.Stores | % {
#-------------------
#Check if PSTs exist
#-------------------
If ($_.FilePath -like "*.PST") {
#------------------------------------------------------------
#If PST exsists then collect data and build CSV column labels
#------------------------------------------------------------
$_ | Select `
#{Expression={$_.DisplayName}; Label="PST Name in Outlook"},`
#{Expression={$_.FilePath}; Label="PST Location/FileName"},`
#{Expression={$_.IsOpen}; Label="PST Open in Outlook"},`
#{Expression={(Get-Item $_.FilePath).Length / 1KB}; Label="PST File Size (KB)"}
$Object | Add-Member -MemberType NoteProperty -Name 'ComputerName' -Value $ComputerName
$Object | Add-Member -MemberType NoteProperty -Name 'UserName' -Value $UserName
#--------------------------------------
#Output PST and User information to log
#--------------------------------------
$Object | Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
#-----------------------------------------
#If no PST exsists then create file NOPSTs
#-----------------------------------------
$Text = "No PSTs Found"
} Else {
$_ | Select $Text | Out-File C:\$UserName-$ComputerName-NOPSTs-$Date.csv
}
}
#---------------
#Closing Outlook
#---------------
Start-Sleep 5
Get-Process | Where {$_.Name -like "Outlook*"} | Stop-Process

Replace this:
$Text = "No PSTs Found"
} Else {
$_ | Select $Text | Out-File C:\$UserName-$ComputerName-NOPSTs-$Date.csv
}
with this:
} Else {
'No PSTs Found' | Out-File C:\$UserName-$ComputerName-NOPSTs-$Date.csv
}

Related

Powershell change/edit value of Object in Variable

I create variables in a ForEach loop using data collected from a CSV file like this:
New-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{
IP = $FlexVPN.'IP-adress'
Information = $FlexVPN.'Information'
Priority = $FlexVPN.'Priority'
RegisteredUp = $RegisteredUp
RegisteredDown = $RegisteredDown
ResponseTime = $Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_.exception | PingFailed }})
What I'm then trying to do is to change the values of RegisteredUp and RegisteredDown depending of the respond of the ping.
I doesn't understand the New-Member stuff which I have tried but faild using.
Now I tried Set-Variable but I don´t get how to only change a Object within the Variable?
Set-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{RegisteredDown = "TESTING"})
I don´t get any errors neither it´s working.
To explain further.
If no respond on ping set Get-Date in RegisteredDown for that Variable.
If respond on ping ser Get-Date in RegisteredUp for that Variable.
I then use if/else to use the result somehow in the next version ;)
Edit
# Clear variables after loop
Remove-Variable * -force -erroraction silentlycontinue
function PingFailed {
# Add date and time when IP-address first didn't responded
$FlexVPN.RegisteredDown = 'AnotherTest'
# If only error should be printed
if($PrintError -eq 'Yes'){Write-Host -ForegroundColor Red $FlexVPN.'IP-adress' "," $FlexVPN.'Information'}
##########################################################################
####################### NO CHANGES ABOVE THIS LINE #######################
##########################################################################
# Choose between printing output or not for all rows in CSV-file [Yes/No]
$PrintOutput = 'Yes'
# Choose between printing out error or not [Yes/No]
$PrintError = 'No'
##########################################################################
####################### NO CHANGES BELOW THIS LINE #######################
##########################################################################
# Import CSV-file to Powershell to use data in code
$FlexVPNlist = Import-Csv -Path $PSScriptRoot\PingIPEmail.csv -Header 'IP-adress', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
Foreach($FlexVPN in $FlexVPNlist) {
New-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{
IP = $FlexVPN.'IP-adress'
Information = $FlexVPN.'Information'
Priority = $FlexVPN.'Priority'
RegisteredDown = 'Test'
ResponseTime = $Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_.exception | PingFailed }})
if($PrintOutput -eq 'Yes'){
if ($host.name -eq 'Windows PowerShell ISE Host') {if ($Result.ResponseTime -eq $null) { $Host.UI.RawUI.BackgroundColor = ($bckgrnd = 'Red') } else { $psISE.Options.RestoreDefaults() }}
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredDown" = 'Test'
}}
}
}
My Second try above works fine until I catch an exeption during ping and goes to my function PingFailed.
I want to run that function when an IP-address doesn´t respond and add Get-Date to RegisteredDown in those cases.
The error I recieve is:
At C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.ps1:49 char:13
+ $FlexVPN.RegisteredDown = 'AnotherTest'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting`
Tried the other code
# Importing a csv
$FlexVPNList = Import-Csv -Path 'C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.csv' -Header 'IP-adress', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
Foreach($FlexVPN in $FlexVPNlist) {
$FlexVPN.GetType() # Object[]
$FlexVPN[0].GetType() # PSCustomObject
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 3 Noteproperties
$FlexVPN | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue 1 -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue 1 -Force
}
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 5 Noteproperties
$Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_ }
if ($Result.ResponseTime -eq $null){
if ($host.name -eq 'Windows PowerShell ISE Host') { $Host.UI.RawUI.BackgroundColor = ($bckgrnd = 'Red') }
$FlexVPN.RegisteredDown = Get-Date
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
if ($Result.ResponseTime -ge '0'){
if ($host.name -eq 'Windows PowerShell ISE Host') { $psISE.Options.RestoreDefaults() }
$FlexVPN.RegisteredUp = Get-Date
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
This code if I understand correctly reuse the same variable for each row in my CSV file.
I want to create one variable for each row (name them the IP-address) so that I can reuse the values stored for as long as the script i running.
Looks like you are overcomplicating things. You can create a new variable (object) like this
$FlexVPN = [PSCustomObject] #{
Information='Test'
}
Show the value of Information
$FlexVPN.Information
Change the value of Information
$FlexVPN.Information = 'AnotherTest'
Show the changed value of Information
$FlexVPN.Information
a valid use case for using new-variable would be if you dynamically create/use variables
Edit
your intent is not actual clear to me but following testbed might get you some new ideas to proceed from
# Mimick importing a csv
$FlexVPN = #'
IP-Adress,Information,Priority
1.1.1.1,FlexVPN,1
2.2.2.2,FlexVPN,2
'# | ConvertFrom-Csv
$FlexVPN.GetType() # Object[]
$FlexVPN[0].GetType() # PSCustomObject
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 3 Noteproperties
$FlexVPN | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue 1 -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue 1 -Force
}
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 5 Noteproperties
Managed what I wanted by doing like this:
# Clear variables after loop
Remove-Variable * -force -erroraction silentlycontinue
# Importing a csv
$FlexVPNList = Import-Csv -Path 'C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.csv' -Header 'IP', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
$FlexVPNList | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue '' -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue '' -Force
Add-Member -InputObject $_ -NotePropertyName 'ResponseTime' -NotePropertyValue '' -Force
}
Foreach($FlexVPN in $FlexVPNlist) {
$Ping = try{ Test-Connection -ComputerName $FlexVPN.IP -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime } catch [System.Net.NetworkInformation.PingException] { $_ }
if($Ping.ResponseTime -ge '0'){
$FlexVPN.RegisteredUp = Get-Date
$FlexVPN.ResponseTime = $Ping.ResponseTime
}
if($Ping.ResponseTime -eq $null){ $FlexVPN.RegisteredDown = Get-Date }
New-Variable -Name $FlexVPN.IP -Value (New-Object PSObject -Property #{
IP = $FlexVPN.IP
Information = $FlexVPN.Information
Priority = $FlexVPN.Priority
RegisteredUp = $FlexVPN.RegisteredUp
RegisteredDown = $FlexVPN.RegisteredDown
ResponseTime = $Ping.ResponseTime
})
[PSCustomObject]#{
"IP address" = $FlexVPN.IP
"Information" = $FlexVPN.Information
"Priority" = $FlexVPN.Priority
"Response time" = $FlexVPN.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
I can now do stuff If computer responded or not!

Multi variable condition output

I'm seeing output issues within the 'Not Found' column, even when DisplayName is not null it's marking mailbox as 'Mailbox Not Found'. Seems ok for the first 5 output lines until until it hits the first 'Cloud' DisplayName then it fills every cell with 'Mailbox Not Found' in the 'Not Found' column.
****if (($mb1 -eq 'No Cloud Mailbox') -and ($mb2 -eq 'No Onprem Mailbox')) { $mb3 = 'Mailbox Not Found' }****
Am I missing something? Any help would be appreciated.
#========
#Get date
#========
$date = Get-Date -format dd-MM-yy
#===========================
#Setting up global variables
#===========================
$allmbadinfo = #()
$mbadinfo = #()
$users = Get-Content D:\import\allrgs.txt
#=================
#Grab mailbox info
#=================
Foreach ($user in $users ) {
$mb1 = Get-RemoteMailbox $User
if ($mb1 -ne $null) { $mb1 = $mb1.DisplayName }
else {
$mb1 = 'No Cloud Mailbox' }
$mb2 = Get-Mailbox $User
if ($mb2 -ne $null) { $mb2 = $mb2.DisplayName }
else {
$mb2 = 'No Onprem Mailbox' }
if (($mb1 -eq 'No Cloud Mailbox') -and ($mb2 -eq 'No Onprem Mailbox')) { $mb3 = 'Mailbox Not Found' }
$imputlist = Write-Output $user
#================================================================================================
#Create new array object and populate information from variables, add table column names and data
#================================================================================================
$mbadinfo = New-Object PSObject
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Cloud' -Value $mb1
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Onprem' -Value $mb2
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Not Found' -Value $mb3
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Imput List' -Value $imputlist
$allmbadinfo += $mbadinfo
}
#======================
# Exporting data to csv
#======================
$allmbadinfo | Export-Csv D:\export\remotembxrgs-$date.csv -NoType -NoClobber
The code will become clearer if you introduce a few more variables so you are not re-using $mb1, $mb2 and $mb3.
The main problem with that is that once your variable $mb3 has been set to 'Mailbox Not Found', it will keep that value forever, since your last if does not state what it should become when the condition is not met.
Try this:
#================================
#Get date and read the users file
#================================
$date = Get-Date -format dd-MM-yy
$users = Get-Content D:\import\allrgs.txt
#=================
#Grab mailbox info
#=================
$allmbadinfo = foreach ($user in $users ) {
# test for remote mailbox
$mb1 = Get-RemoteMailbox $user -ErrorAction SilentlyContinue
$remote = if ($mb1) { $mb1.DisplayName } else { 'No Cloud Mailbox' }
# test for on premise mailbox
$mb2 = Get-Mailbox $user -ErrorAction SilentlyContinue
$onPrem = if ($mb2) { $mb2.DisplayName } else { 'No Onprem Mailbox' }
# if both are $null, set this to 'Mailbox Not Found'
$noMailBox = if (!$mb1 -and !$mb2) { 'Mailbox Not Found' } else { '' }
#================================================================================================
# Create new array object and populate information from variables, add table column names and data
#================================================================================================
# for Powershell v3.0 and newer:
# output an object to get collected in variable $allmbadinfo
[PsCustomObject]#{
'Cloud' = $remote
'Onprem' = $onPrem
'Not Found' = $noMailBox
'Input List item' = $user
}
# for PowerShell versions less than 3.0:
# $mbadinfo = New-Object PSObject
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Cloud' -Value $remote
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Onprem' -Value $onPrem
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Not Found' -Value $noMailBox
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Input List item' -Value $user
# output this object to get collected in variable $allmbadinfo
# $mbadinfo
}
#======================
# Exporting data to csv
#======================
$allmbadinfo | Export-Csv "D:\export\remotembxrgs-$date.csv" -NoType -NoClobber

Updating PSObject hash table in loop

Here is a part code of uploading data by FTP
foreach ($line in $FTPServer)
{
Start-Transcript -Path $results
Write-Host -Object "ftp url: $line"
If (Test-Connection $line -Count 1)
{
Set-FTPConnection -Credentials $FTPCredential -Server $line -Session MySession -UsePassive -ErrorAction SilentlyContinue
$Session = Get-FTPConnection -Session MySession
if($session.UsePassive -eq "True"){$connect="OK"}
else{$connect="!!!-FAIL-!!!"}
foreach ($item in (Get-ChildItem .\Upload))
{
Write-Host -Object "Uploading $item..."
$Send= Add-FTPItem -Session $Session -Path $FTPPlace -LocalPath .\Upload\$item -Overwrite -ErrorAction SilentlyContinue
if($Send.Name -eq $item.Name){$Rec="OK"}
else{$Rec="!!!-FAIL-!!!"}
$array = $line, $item, $connect, $Rec
$FailTable=New-Object -TypeName PSObject -Property ([ordered]#{"FTP Server"=$array[0]; "File"=$array[1];"Connected"=$array[2];"Uploaded"=$array[3]})
$FailTable|Out-File -Append '.\stats.txt'
}
Stop-Transcript
} Else {"$line">> .\DownServers.txt}
}
$Failtable is a hash-table that stores ip of FTP server ($line), name of uploaded file ($item), status if connected ($connect) and upload status ($Rec). The hash-table is piped to file .\stats.txt The problem is in every iteration to .\stats.txt are saved headers like that:
FTP Server File Connected Uploaded
---------- ---- --------- --------
192.168.1.1 ConfigurationDivide.xml OK !!!-FAIL-!!!
FTP Server File Connected Uploaded
---------- ---- --------- --------
192.168.1.1 test.txt OK !!!-FAIL-!!!
So I need that to be one under another and autosized like that:
FTP Server File Connected Uploaded
---------- ---- --------- --------
192.168.1.1 ConfigurationDivide.xml OK !!!-FAIL-!!!
192.168.1.1 test.txt OK !!!-FAIL-!!!
I tryed to put hash-table declaration $FailTable=New-Object -TypeName PSObject -Property ([ordered]#{"FTP Server"=$array[0]; "File"=$array[1];"Connected"=$array[2];"Uploaded"=$array[3]}) before loops and then- in the loops add values ($line, $item,...), but there aren't in hashtable .add method.
Ok, different answer :)
Try this:
$FailTable = #()
$spam = New-Object PSObject
$spam | Add-Member -type NoteProperty -Name 'FTP Server' -Value ""
$spam | Add-Member -type NoteProperty -Name 'File' -Value ""
$spam | Add-Member -type NoteProperty -Name 'Connected' -Value ""
$spam | Add-Member -type NoteProperty -Name 'Uploaded' -Value ""
$FailTable += $spam
$FailTable | Out-File -Append '.\stats.txt'
foreach ($line in $FTPServer)
{
Start-Transcript -Path $results
Write-Host -Object "ftp url: $line"
If (Test-Connection $line -Count 1)
{
Set-FTPConnection -Credentials $FTPCredential -Server $line -Session MySession -UsePassive -ErrorAction SilentlyContinue
$Session = Get-FTPConnection -Session MySession
if($session.UsePassive -eq "True"){$connect="OK"}
else{$connect="!!!-FAIL-!!!"}
foreach ($item in (Get-ChildItem .\Upload))
{
Write-Host -Object "Uploading $item..."
$Send= Add-FTPItem -Session $Session -Path $FTPPlace -LocalPath .\Upload\$item -Overwrite -ErrorAction SilentlyContinue
if($Send.Name -eq $item.Name){$Rec="OK"}
else{$Rec="!!!-FAIL-!!!"}
$spam = New-Object PSObject
$spam | Add-Member -type NoteProperty -Name 'FTP Server' -Value $line
$spam | Add-Member -type NoteProperty -Name 'File' -Value $item
$spam | Add-Member -type NoteProperty -Name 'Connected' -Value $connect
$spam | Add-Member -type NoteProperty -Name 'Uploaded' -Value $Rec
$FailTable += $spam
$FailTable | Select-Object -Last 1 | Format-Table -HideTableHeaders | Out-File -Append '.\stats.txt'
}
Stop-Transcript
} Else {"$line">> .\DownServers.txt}
}
You need to have FailTable created outside the loop, you are creating and appending an entire new PSCustomObject each loop to the file.
try implementing this:
$FailTable = #()
#Example loop
for($i =0;$i -lt 5; $i++){
#Inside the loop add new object to the array
$failTable += #{"FTPServer"=$i;"File"=$i;"Connected"=$i;"Uploaded"=$i}
}
#foreach hashmap in the array cast to a PSCustomObject (which gives you the headers you want)
#and then Select-Object The order you want them in.
$FailTable.foreach{[PSCustomObject]$_} | Select-Object "FTPServer", "File","Connected", "Uploaded" | Out-file test.txt
Edit:
PowerShell v2+ compatible version:
$FailTable = #()
$objTable = #()
#Example loop
for($i =0;$i -lt 5; $i++){
#Inside the loop add new object to the array
$failTable += #{"FTPServer"=$i;"File"=$i;"Connected"=$i;"Uploaded"=$i}
}
#foreach hashmap in the array cast to a PSCustomObject (which gives you the headers you want)
#and then Select-Object The order you want them in.
foreach($fail in $FailTable){
$objTable += New-Object -TypeName PSCustomObject -Property $fail
}
$objTable | Select-Object "FTPServer", "File","Connected", "Uploaded" | Out-file test.txt

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

Powershell script to see currently logged in users (domain and machine) + status (active, idle, away)

I am searching for a simple command to see logged on users on server.
I know this one :
Get-WmiObject -Class win32_computersystem
but this will not provide me the info I need.
It returns :
domain
Manufactureer
Model
Name (Machine name)
PrimaryOwnerName
TotalPhysicalMemory
I run Powershell 3.0 on a Windows 2012 server.
Also
Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique
gives me not the exact answers I need.
I would love to see as well the idle time, or if they are active or away.
In search of this same solution, I found what I needed under a different question in stackoverflow:
Powershell-log-off-remote-session. The below one line will return a list of logged on users.
query user /server:$SERVER
Since we're in the PowerShell area, it's extra useful if we can return a proper PowerShell object ...
I personally like this method of parsing, for the terseness:
((quser) -replace '^>', '') -replace '\s{2,}', ',' | ConvertFrom-Csv
Note: this doesn't account for disconnected ("disc") users, but works well if you just want to get a quick list of users and don't care about the rest of the information. I just wanted a list and didn't care if they were currently disconnected.
If you do care about the rest of the data it's just a little more complex:
(((quser) -replace '^>', '') -replace '\s{2,}', ',').Trim() | ForEach-Object {
if ($_.Split(',').Count -eq 5) {
Write-Output ($_ -replace '(^[^,]+)', '$1,')
} else {
Write-Output $_
}
} | ConvertFrom-Csv
I take it a step farther and give you a very clean object on my blog.
I ended up making this into a module.
There's no "simple command" to do that. You can write a function, or take your choice of several that are available online in various code repositories. I use this:
function get-loggedonuser ($computername){
#mjolinor 3/17/10
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
Maybe you can do something with
get-process -includeusername
If you want to find interactively logged on users, I found a great tip here :https://p0w3rsh3ll.wordpress.com/2012/02/03/get-logged-on-users/ (Win32_ComputerSystem did not help me)
$explorerprocesses = #(Get-WmiObject -Query "Select * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
If ($explorerprocesses.Count -eq 0)
{
"No explorer process found / Nobody interactively logged on"
}
Else
{
ForEach ($i in $explorerprocesses)
{
$Username = $i.GetOwner().User
$Domain = $i.GetOwner().Domain
Write-Host "$Domain\$Username logged on since: $($i.ConvertToDateTime($i.CreationDate))"
}
}
Here is my Approach based on DarKalimHero's Suggestion by selecting only on Explorer.exe processes
Function Get-RdpSessions
{
param(
[string]$computername
)
$processinfo = Get-WmiObject -Query "select * from win32_process where name='explorer.exe'" -ComputerName $computername
$processinfo | ForEach-Object { $_.GetOwner().User } | Sort-Object -Unique | ForEach-Object { New-Object psobject -Property #{Computer=$computername;LoggedOn=$_} } | Select-Object Computer,LoggedOn
}
Another solution, also based on query user, but can handle variations in culture (as far as I can tell) and produces strongly-typed results (i.e. TimeSpan and DateTime values):
# Invoke "query user", it produces an output similar to this, but might be culture-dependant!
#
# USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
# >jantje rdp-tcp#55 2 Active . 3/29/2021 4:24 PM
# pietje 4 Disc 49+01:01 4/14/2021 9:26 AM
$result = (&query 'user' | Out-String -Stream)
# Take the header text and insert a '|' before the start of every HEADER - although defined as inserting a bar after
# every 2 or more spaces, or after the space at the start.
$fencedHeader = $result[0] -replace '(^\s|\s{2,})', '$1|'
# Now get the positions of all bars.
$fenceIndexes = ($fencedHeader | Select-String '\|' -AllMatches).Matches.Index
$timeSpanFormats = [string[]]#("d\+hh\:mm", "h\:mm", "m")
$entries = foreach($line in $result | Select-Object -Skip 1)
{
# Insert bars on the same positions, and then split the line into separate parts using these bars.
$fenceIndexes | ForEach-Object { $line = $line.Insert($_, "|") }
$parts = $line -split '\|' | ForEach-Object { $_.Trim() }
# Parse each part as a strongly typed value, using the UI Culture if needed.
[PSCustomObject] #{
IsCurrent = ($parts[0] -eq '>');
Username = $parts[1];
SessionName = $parts[2];
Id = [int]($parts[3]);
State = $parts[4];
IdleTime = $(if($parts[5] -ne '.') { [TimeSpan]::ParseExact($parts[5], $timeSpanFormats, [CultureInfo]::CurrentUICulture) } else { [TimeSpan]::Zero });
LogonTime = [DateTime]::ParseExact($parts[6], "g", [CultureInfo]::CurrentUICulture);
}
}
# Yields the following result:
#
# IsCurrent Username SessionName Id State IdleTime LogonTime
# --------- -------- ----------- -- ----- -------- ---------
# True jantje rdp-tcp#32 2 Active 00:00:00 3/29/2021 4:24:00 PM
# False pietje 4 Disc 48.11:06:00 4/14/2021 9:26:00 AM
$entries | Format-Table -AutoSize
Team!
I have pretty nice solution to get local session as [PSObject].
Function Get-LocalSession {
<#
.DESCRIPTION
Get local session. Pasre output of command - 'query session'.
#>
[OutputType([PSObject[]])]
[CmdletBinding()]
Param(
)
try {
#region functions
#endregion
$Result = #()
$Output = . query.exe 'session' | select-object -skip 1
#use regex to parse
$pattern = '^(?<This>.)(?<SessionName>[^\s]*)\s*(?<UserName>[a-z]\w*)?\s*(?<Id>[0-9]*)\s*(?<State>\w*)\s*((?<Type>\w*)\s*)?(?<Device>\w*)?'
foreach ( $line in $output ){
$match = [regex]::Matches( $line, $pattern )
if ( $match ){
$PSO = [PSCustomObject]#{
This = $match[0].groups['This'].Value
SessionName = $match[0].groups['SessionName'].Value
UserName = $match[0].groups['UserName'].Value
Id = $match[0].groups['Id'].Value
State = $match[0].groups['State'].Value
Type = $match[0].groups['Type'].Value
Device = $match[0].groups['Device'].Value
}
$Result += $PSO
}
Else {
write-host "Unable to process line [$line] in function [Get-LocalSession]!"
}
}
}
catch {
#Get-ErrorReporting -Trap $PSItem
write-host $PSItem
}
return $Result
}
#Run it
$SessionObject = Get-LocalSession
$SessionObject | format-table -autosize -property *
I have edited mjolinor script to remove duplicate records, and dummy account names such as system, network services,...etc
If you want to get all users
function get-loggedonuser ($computername){
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
if you want to have only domain users
function get-loggedonuser ($computername){
$HST= hostname
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(Get-WmiObject win32_logonsession -ComputerName $computername)
$logon_users = #(Get-WmiObject win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |ForEach-Object {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |ForEach-Object{
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*" -and $session_user[$_.logonid] -notlike "*$HST*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
This is what I just figured out and works out great!
Get-Process -IncludeUserName | Select-Object -Unique | Where-Object {$_.UserName -notlike 'NT AUTHORITY\SYSTEM' -and $_.UserName -notlike 'NT AUTHORITY\NETWORK SERVICE' -and $_.UserName -notlike 'NT AUTHORITY\LOCAL SERVICE'} | Format-Table -Wrap -AutoSize