Script Powershell for MFA call - powershell

I'm having trouble running this script. I am supposed to call using Strong authentication requirements which means users that enabled MFA using the MFA portal will be displayed if I am not mistaken.
Connect-MsolService
$role = getMsolRole -rolename "Company Administrator"
$rm = get-MsolRoleMember -RoleObjectId $role.ObjectId
foreach ($c in $rm)
{
Get-MsolUser -UserPrincipalName $c.EmailAddress | Select DisplayName, UserPrincipalName, #{N="MFA Status"; E={ if($_.StrongAuthenticationRequirements.Count -ne 0) { $_.StrongAuthenticationRequirements.State.toString() } else 'Disabled' }}
Error is
At line:9 char:225
+ ... { $_.StrongAuthenticationRequirements.State.toString() } else 'Disabl ...
+ ~
Missing statement block after 'else' keyword.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingStatementBlockAfterElse
Edited:
If even you can clarify the difference between calling MFA using StrongAuthenticationMethods and using StrongAuthenticationRequirements that will be good. So I can reproduce this code.
Problem with our MFA audit code is that it displays that system administrators are having MFA disabled even though they are claiming they are already enabled.
This is the audit code which returns admins with wildcard administrator are disabled.
Function Get-O365AdminMFAStatus{
$AdminData=#()
$objRole=#()
$Domain = $(get-addomain).dnsroot
$Log = "C:\temp\Audit\$Domain O365 Admin MFA Status $(get-date -f yyyy-MM-dd).csv"
try{
$Roles = Get-MsolRole | where {$_.name -LIKE "*Administrator*"}
$Roles = ($Roles).name
foreach ($Role in $Roles){
$Members = Get-MsolRoleMember -RoleObjectId (Get-MsolRole -RoleName $Role).ObjectId
foreach ($Member in $Members){
$MsUser = $Member | Get-MsolUser
if($MsUser.StrongAuthenticationMethods.Count -eq 0) {
$Enabled = "False"
write-host $Role - $Member.DisplayName "No MFA enabled" -foregroundcolor red
}
Else{
$Enabled = "True"
write-host $Role - $Member.DisplayName "MFA enabled" -foregroundcolor green
}
Try{
$Exist = [bool](Get-mailbox $MsUser.UserPrincipalName -erroraction SilentlyContinue)
if ($Exist){
$MBStats = Get-MailboxStatistics $MsUser.UserPrincipalName
$LastLogon = $MBstats.LastLogonTime
}
Else{
$LastLogon = "N/A"
}
}
Catch{
$LastLogon = "N/A"
}
$objRole = New-Object -TypeName PSObject
$objRole | Add-Member -MemberType NoteProperty -Name "Role Name" -Value $Role
$objRole | Add-Member -MemberType NoteProperty -Name "Display Name" -Value $Member.DisplayName
$objRole | Add-Member -MemberType NoteProperty -Name "UPN" -Value $Member.UserPrincipalName
$objRole | Add-Member -MemberType NoteProperty -Name "Licensed" -Value $Member.IsLicensed
$objRole | Add-Member -MemberType NoteProperty -Name "Last Logon" -Value $LastLogon
$objRole | Add-Member -MemberType NoteProperty -Name "MFA Enabled?" -Value $Enabled
$AdminData += $objRole
}
}
$AdminData | Export-Csv -NoTypeInformation $Log
write-host ""
write-host "CSV Export Complete to $Log" -foregroundcolor yellow
}
Catch{
Write-host "There was an error: $($_.Exception.Message)"
}
}
Get-O365AdminMFAStatus
anyways if you have clarifications with the question I will edit.

From the error you receive it's quite obvious, what you should correct:
Missing statement block after 'else' keyword
You're missing curly braces after else so it should be:
else {'Disabled'}
I checked your cmdlet against my accounts (I have 2FA enabled) and StrongAuthenticationRequirements is empty object for me (checked on many accounts - attempt to clarify below). I think that you should use StrongAuthenticationMethods property instead. It contains information about channels that were configured for 2FA.
Finally, your code would look like this:
foreach ($c in $rm) {
Get-MsolUser -UserPrincipalName $c.EmailAddress | Select DisplayName, UserPrincipalName,
#{N="MFA Status"; E={ if($_.StrongAuthenticationMethods.Count -ne 0) { "$($_.StrongAuthenticationMethods.Count) methods found" } else {'Disabled'} }}
}
But you might notice some entries erroring with such info:
Get-MsolUser : Cannot bind argument to parameter 'UserPrincipalName' because it is null.
To get rid of that, important thing is to filter out ServicePrincipal members from Get-MsolRoleMember (for example, I have RMS and PowerBI Information Service, you might have none or different ones):
foreach ($c in $rm | Where-Object {$_.rolemembertype -eq 'user'}) {
Get-MsolUser -UserPrincipalName $c.EmailAddress | Select DisplayName, UserPrincipalName,
#{N="MFA Status"; E={ if($_.StrongAuthenticationMethods.Count -ne 0) { "$($_.StrongAuthenticationMethods.Count) methods found" } else {'Disabled'} }}
}
Clarification about StrongAuthenticationMethods and StrongAuthenticationRequirements
From what I read here it seems like StrongAuthenticationRequirements applies for per-user MFA. If your tenant is using Conditional Access based MFA, that property might be empty (checked on my tenant). So I guess StrongAuthenticationMethods is more reliable.
NOTE: I also tested the part of long code you have posted and it works correctly for me. And you have a typo in getMsolRole - should be Get-MsolRole

Related

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"
}

Checking Admins who are MFA enabled using Strong Authentication Methods

There is an upcoming audit of our institution and I just need to verify this script.
because our system admins are saying they are MFA enabled but when I run the script it displays that they are not. So upon further checking they might be enabled using StrongAuthenticationRequirements not through Strong Authentication methods as per the the script or code. Please clarify or enlighten me as I am not a pro when it comes to powershell.
Function Get-O365AdminMFAStatus{
$AdminData=#()
$objRole=#()
$Domain = $(get-addomain).dnsroot
$Log = "C:\temp\Audit\$Domain O365 Admin MFA Status $(get-date -f yyyy-MM-dd).csv"
try{
$Roles = Get-MsolRole | where {$_.name -LIKE "*Administrator*"}
$Roles = ($Roles).name
foreach ($Role in $Roles){
$Members = Get-MsolRoleMember -RoleObjectId (Get-MsolRole -RoleName $Role).ObjectId
foreach ($Member in $Members){
$MsUser = $Member | Get-MsolUser
if($MsUser.StrongAuthenticationMethods.Count -eq 0) {
$Enabled = "False"
write-host $Role - $Member.DisplayName "No MFA enabled" -foregroundcolor red
}
Else{
$Enabled = "True"
write-host $Role - $Member.DisplayName "MFA enabled" -foregroundcolor green
}
Try{
$Exist = [bool](Get-mailbox $MsUser.UserPrincipalName -erroraction SilentlyContinue)
if ($Exist){
$MBStats = Get-MailboxStatistics $MsUser.UserPrincipalName
$LastLogon = $MBstats.LastLogonTime
}
Else{
$LastLogon = "N/A"
}
}
Catch{
$LastLogon = "N/A"
}
$objRole = New-Object -TypeName PSObject
$objRole | Add-Member -MemberType NoteProperty -Name "Role Name" -Value $Role
$objRole | Add-Member -MemberType NoteProperty -Name "Display Name" -Value $Member.DisplayName
$objRole | Add-Member -MemberType NoteProperty -Name "UPN" -Value $Member.UserPrincipalName
$objRole | Add-Member -MemberType NoteProperty -Name "Licensed" -Value $Member.IsLicensed
$objRole | Add-Member -MemberType NoteProperty -Name "Last Logon" -Value $LastLogon
$objRole | Add-Member -MemberType NoteProperty -Name "MFA Enabled?" -Value $Enabled
$AdminData += $objRole
}
}
$AdminData | Export-Csv -NoTypeInformation $Log
write-host ""
write-host "CSV Export Complete to $Log" -foregroundcolor yellow
}
Catch{
Write-host "There was an error: $($_.Exception.Message)"
}
}
Get-O365AdminMFAStatus
what does eq-0 mean ($MsUser.StrongAuthenticationMethods.Count -eq 0)
-eq is one of the Comparison Operators in PowerShell that stands for Equal. In this case it means:
If ($MsUser.StrongAuth...... is empty)
e.g. If there are no StrongAuthenticationMethods, then{ do something }
There are many Comparison Operators.
Take a look at [https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-5.1][1]

Extracting ADUser from a list of AD groups in different domain

I have this code that extracts ADuser information from a list of AD groups, However there are some AD groups which are located on a different domain. The problem is that the code only extracts the ADuser information of the First Domain1 mentioned on the domain list, it does not go to the other domain and extract the information of the AD group that is in a different domain.
$outFile=".\Output.csv"
$report=#()
$ADGroups = Get-Content ".\ADGroups.txt"
$Domains = #("Domain1.com","Domain2.com","Domain3.com","Domain4.com","Domain5.com")
foreach ($ADGroup in $ADGroups)
{
try{
Foreach ($Domain in $Domains)
{
$ADGroupMem = Get-ADGroupMember -Identity $ADGroup -server $Domain
ForEach ($Member in $ADGroupMem){
$ADUser = $Member | Get-ADUser -Properties SamAccountName,Department,Title,Manager,l,Mobile
[PSCustomObject]#{
UserID = $ADUser.SamAccountName
Group = $ADGroup
City = $ADUser.l
}
$localAdminsReport = new-object PSObject
$localAdminsReport | add-member -membertype NoteProperty -name "UserID" -Value $ADUser.SamAccountName
$localAdminsReport | add-member -membertype NoteProperty -name "Group" -Value $ADGroup
$localAdminsReport | add-member -membertype NoteProperty -name "City" -Value $ADUser.l
#Write output into .csv file
#write-host $report -ForegroundColor cyan
write-host $Domain
$report +=$localAdminsReport
$report|Export-Csv -Path $outFile -NoTypeInformation
}
}
}
Catch{}
}
When the report is extracted it has only extracted the information from the first Domain1 AD groups and leaves out other AD groups that are located in Domain2, Domain3.....
I've reworked your script. As mentioned in above comment you should use the -Append switch of the Export-Csv cmdlet. The other thing is that your catch clause is empty, so you won't recognize any errors. I would also recommend setting the $ErrorPreference to Stop otherwise cmdlets won't throw an error. Here is the reworked code:
$outFile = ".\Output.csv"
$report = #()
$ADGroups = Get-Content ".\ADGroups.txt"
$Domains = #("Domain1.com", "Domain2.com", "Domain3.com", "Domain4.com", "Domain5.com")
$ErrorActionPreference = "Stop"
foreach ($ADGroup in $ADGroups) {
try {
Foreach ($Domain in $Domains) {
$ADGroupMem = Get-ADGroupMember -Identity $ADGroup -server $Domain
ForEach ($Member in $ADGroupMem) {
$ADUser = $Member | Get-ADUser -Properties SamAccountName, Department, Title, Manager, l, Mobile
$localAdminsReport = new-object PSObject
$localAdminsReport | add-member -membertype NoteProperty -name "UserID" -Value $ADUser.SamAccountName
$localAdminsReport | add-member -membertype NoteProperty -name "Group" -Value $ADGroup
$localAdminsReport | add-member -membertype NoteProperty -name "City" -Value $ADUser.l
$report += $localAdminsReport
$report | Export-Csv -Path $outFile -NoTypeInformation -Append
}
}
}
Catch {
Write-Error "Exception occurred $_"
}
}
$ErrorActionPreference = "SilentlyContinue"
$report | ForEach-Object { Write-Host $_}
At the of the code the content $report is dumped for debugging purposes. Check if the $report has a difference to the exported CSV file.
You can also debug your code via Visual Studio Code, via command line degguer, or via the Powershell ISE.
Hope that helps.
This worked for me. Thank you Moerwald
$outFile = ".\Output.csv"
$report = #()
$ADGroups = Get-Content ".\ADGroupslist.txt"
foreach ($ADGroup in $ADGroups) {
$Domains = #("Domain1","Domain2","Domain3","Domain4","Domain5")
foreach ($Domain in $Domains)
{
try
{
$ADGroupMem = Get-ADGroupMember -Identity $ADGroup -server $Domain
ForEach ($Member in $ADGroupMem) {
$ADUser = $Member | Get-ADUser -Properties SamAccountName, l
[PSCustomObject]#{
UserID = $ADUser.SamAccountName
Group = $ADGroup
City = $ADUser.l
}
$localAdminsReport = new-object PSObject
$localAdminsReport | add-member -membertype NoteProperty -name "UserID" -Value $ADUser.SamAccountName
$localAdminsReport | add-member -membertype NoteProperty -name "Group" -Value $ADGroup
$localAdminsReport | add-member -membertype NoteProperty -name "City" -Value $ADUser.l
$report += $localAdminsReport
$report | Export-Csv -Path $outFile -NoTypeInformation -Append
write-host "$ADGroup : $Domain" -ForegroundColor cyan
}
}
catch
{
Write-Error "Exception occurred $_"
}
}
}
$ErrorActionPreference = "Continue"
$report | ForEach-Object { Write-Host $_}

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

Exporting info from PS script to csv

This is a powershell/AD/Exchange question....
I'm running a script against a number of Users to check some of their attributes however I'm having trouble getting this to output to CSV. The script runs well and does exactly what I need it to do, the output on screen is fine, I'm just having trouble getting it to directly export to csv.
The input is a comma seperated txt file of usernames (eg "username1,username2,username3")
I've experimented with creating custom ps objects, adding to them and then exporting those but its not working....
Any suggestions gratefully received..
Thanks
George
$array = Get-Content $InputPath
#split the comma delimited string into an array
$arrayb = $array.Split(",");
foreach ($User in $arrayb)
{
#find group memebership
Write-Host "AD group membership for $User"
Get-QADMemberOf $User
#Get Mailbox Info
Write-Host "Mailbox info for $User"
Get-Mailbox $User | select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
#get profile details
Write-Host "Home drive info for $User"
Get-QADUser $User| select HomeDirectory,HomeDrive
#add space between users
Write-Host ""
Write-Host "******************************************************"
}
Write-Host "End Script"
EDITED....
Methods I have tried for exproting (showing only the for loop/export code)
Method1
$AllData = #()
foreach ($User in $arrayb)
{
#set title for this user
#Write-host "Details for $User"
#find out their group memebership
Write-Host "AD group membership for $User"
$AdMemberOf = Get-QADMemberOf $User
Write-Host "ad completed"
Write-Host ""
Write-Host ""
#Get Mailbox Info
Write-Host "Mailbox info for $User"
$ExInfo = Get-Mailbox $User | select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
Write-Host "ex completed"
Write-Host ""
Write-Host ""
#get profile details
Write-Host "Home drive info for $User"
$HomeInfo = Get-QADUser $User| select HomeDirectory,HomeDrive
Write-Host "home drive completed"
#add space between users
Write-Host ""
Write-Host "******************************************************"
$ReturnedObj = New-Object PSObject
$ReturnedObj | Add-Member NoteProperty -Name "AD Group Membership for $User" -Value $AdMemberOf
$ReturnedObj | Add-Member NoteProperty -Name "Exchange details for $User" -Value $ExInfo
$ReturnedObj | Add-Member NoteProperty -Name "Home drive info for $User" -Value $HomeInfo
Write-Host $ReturnedObj
$AllData += $ReturnedObj
}
Write-Host "starting csv export"
Write-Output $AllData |export-csv -Path $OutputPath -notype -force
Write-Host "End Script"
Method2
$ExportInfo = #()
foreach ($User in $arrayb)
{
$UserInfo = New-Object System.Object
#set title for this user
$ExportInfo += "Details for $User"
#Get Mailbox Info
$ExportInfo += Get-Mailbox $User
#find out their group memebership
$ExportInfo += Get-QADMemberOf $User
#get profile details
$ExportInfo += Get-QADUser $User| select HomeDirectory,HomeDrive
#add space between users
$ExportInfo += ""
$ExportInfo += ""
}
$ExportInfo | Export-Csv -Path $OutputPath ;
EDITED:
Thanks to suggestion from Stej I now have the code below... Still doesn't work correctly. I have added a check to confirm the user exists in AD as well. First problem is with getting a users AD group membership - if I put a break point in and look at the value of a a specific users AD membership, the value in teh varialbe is error "Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value." No idea what's causing this, so I've just ignored it for now and commented out those lines for AD membership to concentrate on gettng the rest working.
With the lines commented out, the script runs fine and again, with a breakpoint at the bottom I can look at the value of $ExportInfo and they all look fine, ie they have all been saved into the variable correctly. However, it won't output. As you can see, I put a command to get $ExportInfo written to screen but that doesn't show anything. When it attempts to execute the Export line (ie "Export-Csv $ExportInfo -Path $OutputPath") it errors with:
"Cannot convert 'System.Object[]' to the type 'System.Char' required by parameter 'Delimiter'. Specified method is not supported.
At :line:80 char:10
+ Export-Csv <<<< $ExportInfo -Path $OutputPath"
I chnaged the line to "$ExportInfo | Export-Csv -Path $OutputPath" and it now exports to CSV... No idea why??? Two issues though... as noted above, AD groups aren't working and the field Email Addresses (which should return something like { SMTP:j.smith#domain.com, smtp: j.smith#domain.com.au, smtp: smithj#exchange.domain.com.au and SIP:j.smith#domain.com.au}) just shows up in the CSV as "Microsoft.Exchange.Data.ProxyAddressCollection". Again, checking in $ExportInfo, the addresses are there....
Thanks
$ExportInfo = #()
foreach ($User in $arrayb)
{
$CheckUser = Get-QADUser -Name $User
if (!$CheckUser)
{
$CountUser++
Write-Warning "############################################"
Write-Warning "$user not found in AD"
Write-Warning "############################################"
}
else
{
$CountUser++
$UserInfo = New-Object System.Object
#find out their group memebership
Write-Host "AD group membership for $User"
#$Temp = Get-QADMemberOf $User
#$UserInfo | Add-Member NoteProperty -Name "AD Group Membership" -Value $Temp.Name
#set title for this user
#Write-host "Details for $User"
#Get Mailbox Info
Write-Host "Mailbox info for $User"
$Temp = Get-Mailbox $User #| select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
$UserInfo | Add-Member NoteProperty -Name "ServerName" -Value $Temp.ServerName
$UserInfo | Add-Member NoteProperty -Name "Database" -Value $Temp.Database
$UserInfo | Add-Member NoteProperty -Name "Email Addresses" -Value $Temp.EmailAddresses
$UserInfo | Add-Member NoteProperty -Name "Primary SMTP" -Value $Temp.PrimarySmtpAddress
$UserInfo | Add-Member NoteProperty -Name "Windows Email Address" -Value $Temp.WindowsEmailAddress
#$ReturnedObj | Add-Member NoteProperty -Name
#get profile details
Write-Host "Home drive info for $User"
$Temp = Get-QADUser $User #| select HomeDirectory,HomeDrive
$UserInfo | Add-Member NoteProperty -Name "Home Directory Location" -Value $Temp.HomeDirectory
$UserInfo | Add-Member NoteProperty -Name "Home Drive Mapped To" -Value $Temp.HomeDrive
#add space between users
Write-Host ""
Write-Host "******************************************************"
$ExportInfo += $UserInfo
}#end else
}
Write-Host "blah"
Write-Host $ExportInfo
Export-Csv $ExportInfo -Path - $OutputPath
Write-Host "Number of Users processed: $CountUser"
Is there any error message? Is there something in $error[0]? What does it mean that it is not working?
Edited:
In your second method you create $UserInfo object, but you don't use it. I guess it was intended to be added to $ExportInfo. Instead of this you add bare strings to $ExportInfo and that's why you gave such a strange content of your csv file.
How it should be done correctly:
$UserInfo = New-Object System.Object
$ReturnedObj | Add-Member NoteProperty -Name "User Name" -Value $user.Name
$ReturnedObj | Add-Member NoteProperty -Name "User otherValue" -Value $user.othValue
#Get Mailbox Info
$mailBox = Get-Mailbox $User
$ReturnedObj | Add-Member NoteProperty -Name "User Mail box size" -Value $mailBox.Size
$ReturnedObj | Add-Member NoteProperty -Name "User Mail box -count of messages" -Value $user.countOfMessages
... # and so on
$ExportInfo += $ReturnedObj
Note that the objects that you want to export to csv have to have properties of type strings, integers, bools etc. Not composed objects like $User or the ones returned by Get-Mailbox $User. The values in csv have to be primitive. That's the reason.
That applies for your first example where you add NoteProperty with value $AdMemberOf. That is object itself that can not be exported to csv. You have to create bunch of properties for every interesting property of $AdMemberOf.
Turned out I needed to use some foreach loops to get the info I needed.... So:
foreach($user in $array)
{
$temp = Get-QADUser -Name $User
if (!$temp)
{
$log += "????????????????" + "`n"
$log += "$user not found in AD" + "`n"
$log += "????????????????" + "`n"
}
else
{
#find out their group memebership
$log += "AD group membership for $User" + "`n"
$temp = Get-QADMemberOf $User
foreach ($drive in $temp)
{
$temp2 = $drive
$temp2 = $temp2.Substring(6)
$log += "`t" + $drive + "`n"
}#end foreach for drive loop
$temp = Get-Mailbox $User
if ($temp.RecipientType -like "UserMailbox")
{
$log += "Mailbox info for $User" + "`n"
#$log += Get-Mailbox $User | select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
$log += "Email Server: " + $temp.ServerName + "`n"
$log += "Email Database: " + $temp.Database + "`n"
$log += "Primary SMTP: " + $temp.PrimarySmtpAddress + "`n"
$log += "Windows Email Address: "+ $temp.WindowsEmailAddress + "`n"
#$log += "`n"
foreach ($e in $temp.EmailAddresses)
{
$log += "`t" + "Email Addresses: " + $e + "`n"
}
}
else
{
$log += "########" + "`n"
$log += "$User is not a MailboxUser, IE no Exchange Mailbox" + "`n"
$log += "########" + "`n"
}
$log += "Home drive info for $User" + "`n"
$temp = Get-QADUser $User| select HomeDirectory,HomeDrive
$log += "Home Directory: " + $temp.HomeDirectory + "`n"
$log += "Home Drive Letter: " + $temp.HomeDrive + "`n"
$tempvar = [string] $temp.HomeDirectory
if ($tempvar -eq "")
{
$noHomedirectory += $User + "`n"
$countNoHOmeDirectory ++
}
}#end of the main if/else to determine if the AD account exists
$OutputPath = "C:\SomeFolder\"+"User_Report_"+([datetime]::Now).tostring("yyyyMMddhhmmss")+".txt"
$log | Out-File -FilePath $OutputPath
I then dump all the specifc logs (eg $noHomeDirectory etc) in the body of an email, attach the complete log as exported above and send it to myself.
Thanks for everyone's suggestions above and sorry for the delay in posting back the answer...