PowerShell: Successful MFA logins with Log Analytics

Despite of the intent, nowadays I don’t get enough time to get back to blogging. It’s not the case that content is not there but in a way, so many things happening that its hard to decide if anything that important to write about.

Came across a typical issue of getting O365 login reports using PowerShell than usual GUI way, which is not only painfully slow but doesn’t has option to filter out basis user names or target specific list of applications. To solve the issue, have created a small script which may help you on the particular issue if you have OMS subscription for log analytics

# Author Nitish Kumar
# V1.0
# 04/04/2020
# Pre-requisites: This needs PowerShell Module az [installl-module az -scope currentuser] | AzureRM module should be uninstalled and .Net should be 4.7.2 & above

# Connect to Azure Account
Connect-AzAccount -TenantId <xxxxxxxxx> -Subscription <yyyyyyyyyy>

# Would need to find the correct Log Analytics instance (We have two instances and using 1st one)
$wks = Get-AzOperationalInsightsWorkspace

$Sumamry = @()

# Number of days input with validator
Do {	$nDays = Read-host "`rLogs for how many days should be check for signIns (only numeric and less than 30) " } While(!($nDays -match '^[0-9]+$'))

# In case if you looking for logins into specific app
$sApp = Read-host "`rAny specific App for which logs needed? (Exact name required) if no, then press enter " 

# Take list of users from txt file
$sUserIn = Read-host "`rAny specific user for which logs needed? Exact UPNs required? if yes special a text file containing the same, if no then press enter " 
If($sUserIn.Length -ge 1) { 

	$AllSpecificGroupUsers = (Get-AzureADGroup -SearchString <group_name> | Get-AzureADGroupMember -All 1).UserPrincipalname

	$Users = (Get-Content $sUserIn) | %{Get-AzureADUser -searchstring $_} | Select-Object DisplayName, UserPrincipalName, Mail, AccountEnabled
	$UserReport = @()

	ForEach($User in $Users){
		if($AllSpecificGroupUsers -contains $User.UserPrincipalName){		$GPEnabled = "True"	} 
		else {		$GPEnabled = "False"	}
		$infoObject = New-Object PSObject		
		$infoObject = $User
		Add-Member -inputObject $infoObject -memberType NoteProperty -name "GPEnabled" -value $GPEnabled
		$UserReport += $infoObject
	$UserReport | Select-Object DisplayName, UserPrincipalName, Mail, AccountEnabled, GPEnabled | Export-csv -nti $env:userprofile\desktop\Users_$((Get-Date).ToString('MM-dd-yyyy_hhmm')).csv
	$sUser = (Get-Content $sUserIn) -join " "	

$LoopCount = 6 * $nDays

For ($i=0; $i -le $LoopCount; $i++) {
	Write-Progress -activity "Getting logs " -status "Percent Done: " -PercentComplete (($i / $LoopCount)  * 100) -CurrentOperation " Timespan $((Get-Date).Addhours(-($(4 * $i)))) - $((Get-Date).Addhours(-($(4 * ($i+1)))))"	
	$time1 = 4 * ($i)
	$time2 = 4 * ($i+1)		
	If($sUser.Length -ge 1){
		If($sApp.Length -ge 1)	{						
			$sQuery = "SigninLogs | where  '" + $sUser +"' contains UserPrincipalName and AppDisplayName == '" + $sApp +"' and ConditionalAccessStatus == 'success' | where TimeGenerated > ago(" + $time2 + "h) and TimeGenerated < ago("+ $time1 +"h)| order by TimeGenerated asc"			
		Else {				
			$sQuery = "SigninLogs | where  '" + $sUser +"' contains UserPrincipalName and ConditionalAccessStatus == 'success' | where TimeGenerated > ago(" + $time2 + "h) and TimeGenerated < ago("+ $time1 +"h)| order by TimeGenerated asc"			
	Else {
		If($sApp.Length -ge 1)	{
			$sQuery = "SigninLogs | where AppDisplayName == '" + $sApp +"' and ConditionalAccessStatus == 'success' | where TimeGenerated > ago(" + $time2 + "h) and TimeGenerated < ago("+ $time1 +"h)| order by TimeGenerated asc"
		Else {	
			$sQuery = "SigninLogs | where ConditionalAccessStatus == 'success' | where TimeGenerated > ago(" + $time2 + "h) and TimeGenerated < ago(" + $time1 + "h)| order by TimeGenerated asc"
	$sResponse = Invoke-AzOperationalInsightsQuery -WorkspaceId $wks[0].CustomerId -Query $sQuery 

	# Collect the data
	$Summary +=$sResponse.Results

$Summary | Select-Object userDisplayName,UserPrincipalName, OperationName, Category, AppDisplayName, ClientAppUsed, ConditionalAccessStatus, DeviceDetail, IPAddress, Type, TimeGenerated -Unique | Export-csv -nti $env:userprofile\desktop\AzureSigninLogs_$((Get-Date).ToString('MM-dd-yyyy_hhmm')).csv


Applying passport in India – Still a stupid affair in name of police verification

Not to say things don’t change in India as there been some rapid changes in last decade. We have moved past of LPG lines, we have moved past of accepting daily power cuts as fate in a developing nation, we have moved past of considering trains as only way of transport due to higher prices of planes and bad roads… but as much things change, some still remain the same. Indian red tape and police system can be considered something like that.

Its been almost 3.5 yrs since I wrote about my last experience with applying passport in india and proudly bragged that situation seem to have improved much better than the past, but only to find from recent experience that the systematic nonsense continues to harass citizens same way since always.

In my last attempt for passport, I noticed only one stupidity that PSK refused to take AADHAAR photocopy on counter while this is just a number, which can be verified by the fingerprint scan or other means anyway installed inside PSK and this time it came about police verification.

Continue reading “Applying passport in India – Still a stupid affair in name of police verification”

Powershell: Servers inventory

Quite many months since when I might have written a blog post. Life been quite busy at workplace and home end both. Thought to share yet another PowerShell script, which might be quite useful for System Admins.

This covers CPU, Memory, Network cards, Disks etc and other parameters can be added accordingly.

You might run into issues with some servers if WMI related ports not open on them. Below CMD should help on that

netsh advfirewall firewall set rule group=”Windows Remote Management” new enable=yes

# Author : Nitish Kumar
# Outputs the results to a csv file.
# version 1.0

# The conditions can be tweaked to find some specific set of computers, may be workstations or a particular OS or may be Security group member
$servers = Get-ADComputer -f {OperatingSystem -like "*Server*"} -properties IPv4Address, OperatingSystem | select-object Name, IPv4Address, OperatingSystem

#Run the commands for each server in the list
$infoColl = @()
$i = 0
Foreach ($s in $servers)
Write-Progress -activity "Getting Server Details ($i / $($Servers.Count)):" -status "Percent Done: " -PercentComplete (($i / $Servers.Count)  * 100) -CurrentOperation "Now processing $($s.Name)"

$CPUInfo = Get-WmiObject Win32_Processor -ComputerName $s.Name
$PhysicalMemory = Get-WmiObject CIM_PhysicalMemory -ComputerName $s.Name | Measure-Object -Property capacity -Sum | % { [Math]::Round(($_.sum / 1GB), 2) }
$NetworkInfo = Get-wmiobject Win32_networkadapter -ComputerName $s.Name | ?{$_.MACAddress -ne $null -AND $_.PhysicalAdapter -eq $true}
$DiskInfo = Get-wmiobject Win32_LogicalDisk -ComputerName $s.Name
$SerialNumber = (Get-WmiObject Win32_BIOs -ComputerName $s.Name).SerialNumber
$MakeInfo = Get-WmiObject Win32_ComputerSystem -ComputerName $s.Name
$NICSpeed = (($NetworkInfo.Speed | %{([Math]::Round(($_ / 1GB), 2))}) -Join " Gbps,") + " Gbps"
$DiskSizes = (($DiskInfo.size | %{([Math]::Round(($_ / 1GB), 2))}) -join " GB,") + " GB"
$DiskFreeSizes = (($DiskInfo.FreeSpace | %{([Math]::Round(($_ / 1GB), 2))}) -join " GB,") + " GB"

$infoObject = New-Object PSObject
#The following add data to the infoObjects.
Add-Member -inputObject $infoObject -memberType NoteProperty -name "ServerName" -value $s.Name
Add-Member -inputObject $infoObject -memberType NoteProperty -name "IP Address" -value $s.IPv4Address
Add-Member -inputObject $infoObject -memberType NoteProperty -name "SerialNumber" -value $SerialNumber
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Manufacturer" -value $MakeInfo.Manufacturer
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Model" -value $MakeInfo.Model
Add-Member -inputObject $infoObject -memberType NoteProperty -name "OperatingSystem" -value $s.OperatingSystem
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Processor" -value ($CPUInfo.Name -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "CPU Model" -value ($CPUInfo.Description -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "CPU Manufacturer" -value ($CPUInfo.Manufacturer -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "CPU PhysicalCores" -value ($CPUInfo.NumberOfCores -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "CPU LogicalCores" -value ($CPUInfo.NumberOfLogicalProcessors -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "OS_Name" -value $OSInfo.Caption
Add-Member -inputObject $infoObject -memberType NoteProperty -name "TotalPhysical_Memory_GB" -value $PhysicalMemory
Add-Member -inputObject $infoObject -memberType NoteProperty -name "NIC count" -value ($NetworkInfo | Measure-object).Count
Add-Member -inputObject $infoObject -memberType NoteProperty -name "NIC Name" -value ($NetworkInfo.NetConnectionID -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "NIC Type" -value ($NetworkInfo.ProductName -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "NIC Manufacturer" -value ($NetworkInfo.Manufacturer -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "NIC MAC Address" -value ($NetworkInfo.MACAddress -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "NIC Speed" -value $NICSpeed
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Drives Count" -value ($DiskInfo | Measure-object).Count
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Drives Letters" -value ($DiskInfo.DeviceID -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Drive Volume Names" -value ($DiskInfo.VolumeName -join ",")
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Drive Sizes" -value $DiskSizes
Add-Member -inputObject $infoObject -memberType NoteProperty -name "Drive free space Sizes" -value $DiskFreeSizes

$infoObject #Output to the screen for a visual feedback.
$infoColl += $infoObject
catch {
Write-host "Issue in data collection from $($s)"
$infoColl | Export-Csv -path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation #Export the results in csv file.

PowerShell: File Share inventory for all DFS shares – Permissions and Size

Managing file servers in a large environment where there are more than one administrators might be a tough job if you do not have audit tool keeping track of changes in File Shares. Similar problematic part is migrating stuff from one server to another. DFS helps, but how to document everything??

What about PowerShelling it??

# Author : Nitish Kumar
# Performs an audit of file shares
# Outputs the results to a csv file.
# version 1.0
# 26 March 2018

$AllDFS = (Get-DFSNRoot | ?{$_.State -eq 'Online'}).Path
$DFSInfo = @()

ForEach($DFS in $AllDFS){
	$DFS = $DFS + "\*"
	$DFSInfo += Get-DFSNFolder -path  $DFS | Get-DFSNFolderTarget | Select-object Path, Targetpath

Function GetShareDetails
    Param (

	$array = @()
	$Share = Get-WmiObject -class Win32_Share -Computername $Server |Where-object {$_.name -eq $ShareName}
	$BaseShare = "\\" + $Server + "\" + $ShareName
	$Postfix = $FullPath.Replace($BaseShare,"")	

	Try { $ShareSec = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -ComputerName $Server  -filter "Name='$($Share.Name)'"  -ErrorAction silentlycontinue  }
	Catch {}

	if ($shareSec) {
		$SD = $sharesec.GetSecurityDescriptor()
		$ShareInfo = $SD.Descriptor.DACL | % {$_ | select AccessMask,AceFlags,AceType,@{e={$_.trustee.Name};n='User'},@{e={$_.trustee.Domain};n='Domain'},@{e={$_.trustee.SIDString};n='SID'}}

		#Convert the current output into something more readable
		Switch ($ShareInfo.AceType)
			0 {$AceType = "Allow"}
			1 {$AceType = "Deny"}
			2 {$AceType = "Audit"}
		$ShareInfo | Add-Member NoteProperty AccessType $AceType 

		#Convert the current output into something more readable
		Switch ($ShareInfo.AccessMask)
			2032127 {$AccessMask = "FullControl"}
			1179785 {$AccessMask = "Read"}
			1180063 {$AccessMask = "Read, Write"}
			1179817 {$AccessMask = "ReadAndExecute"}
			-1610612736 {$AccessMask = "ReadAndExecuteExtended"}
			1245631 {$AccessMask = "ReadAndExecute, Modify, Write"}
			1180095 {$AccessMask = "ReadAndExecute, Write"}
			268435456 {$AccessMask = "FullControl (Sub Only)"}
			default {$AccessMask = $DACL.AccessMask}
		$ShareInfo | Add-Member NoteProperty Access $AccessMask
	} else {
		Write-Warning "Specified share not exist or you may not have sufficient rights to access them!"

	$SharePath = $Share.Path + $Postfix
	$Details = Get-Item $SharePath
	$Files = Get-ChildItem -Recurse $SharePath -ErrorAction SilentlyContinue | Where-Object {!$_.PSIsContainer}
	$SharePermissions  = (Get-ACL -path $SharePath).Access | Select-object IdentityReference, FileSystemRights, AccessControlType
	$Size = [Math]::Round(($Files | Measure-Object Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB, 2)
	$obj = New-Object PSObject
	$obj |Add-Member -MemberType NoteProperty -Name "ServerName" $env:Computername
	$obj |Add-Member -MemberType NoteProperty -Name "ShareName" $Share.Name
	$obj |Add-Member -MemberType NoteProperty -Name "Path" $SharePath
	$obj |Add-Member -MemberType NoteProperty -Name "PermittedOnShare" $($ShareInfo.User -join ',')
	$obj |Add-Member -MemberType NoteProperty -Name "SharePermissionType" $($ShareInfo.AccessType -join ',')
	$obj |Add-Member -MemberType NoteProperty -Name "SharePermissionLevel" $($ShareInfo.Access -join ',')
	$obj |Add-Member -MemberType NoteProperty -Name "Allowedusers" $($SharePermissions.IdentityReference -join ',')
	$obj |Add-Member -MemberType NoteProperty -Name "Accesstype" $($SharePermissions.FileSystemRights -join ',')
	$obj |Add-Member -MemberType NoteProperty -Name "SizeinMB" $Size
	$obj |Add-Member -MemberType NoteProperty -Name "Filescount" $Files.count
	$obj |Add-Member -MemberType NoteProperty -Name "DateModified" $Details.LastWritetime
	$obj |Add-Member -MemberType NoteProperty -Name "DateLastAccessed" $Details.LastAccesstime
	$array +=$obj
	$array | select-object ServerName,ShareName,Path,PermittedOnShare,SharePermissionType,SharePermissionLevel,Allowedusers,AccessType,SizeinMB, Filescount, DateModified, DateLastAccessed

$Result = @()
$k = 0

ForEach ($DFSObject in $DFSInfo)
	$Server = [String]$DFSObject.TargetPath.Split("\")[2]
	$Sharename = [String]$DFSObject.TargetPath.Split("\")[3]
	$FullPath = [String]$DFSObject.TargetPath
	Write-Progress -activity "Getting File Shares Details ($k / $($DFSInfo.Count)):" -status "Percent Done: " -PercentComplete (($k / $DFSInfo.Count)  * 100) -CurrentOperation "Now processing $($DFSObject.Path)"
	$temp = Invoke-Command -ComputerName $Server -ScriptBlock ${Function:GetShareDetails} -ArgumentList $Server, $ShareName, $FullPath -ErrorAction Continue
	$temp | Add-Member -MemberType NoteProperty "DFSShareName" -Value $DFSObject.Path
	$temp |  select-object ServerName,DFSShareName,ShareName,Path,PermittedOnShare,SharePermissionType,SharePermissionLevel,Allowedusers,AccessType,SizeinMB, Filescount,DateModified, DateLastAccessed
	$Result += $temp
$Result | Select-object ServerName,DFSShareName,ShareName,Path,PermittedOnShare,SharePermissionType,SharePermissionLevel,Allowedusers,AccessType,SizeinMB, Filescount,DateModified, DateLastAccessed|export-csv -nti $env:userprofile\desktop\FileSharedata_$(get-date -Uformat "%Y%m%d-%H%M%S").csv

Share your feedback

PowerShell: Quickly finding source of Brute Force attack on O365 Tenant

A small PowerShell Script to quickly find out source IPs in case of a brute force attack on O365 Infra. This can save manual efforts and can improve turnaround time to mitigate the issue in Infrastructures which still not using MFA or ExtraNetLockOut. This is just a basic code. One can set email alerts basis the thresholds or even can write a custom event on any of the server to be picked by SCOM or OMS.

Pre-requisites: Auditing should be enabled on Federation servers

# To investigate Account lockouts on Federation Servers
# Author: Nitish Kumar
# v1.0

# Thresholds
$Period = 1
$LockedAccountsThreshold = 50

#federation Servers
$FServers = ("XYZ01", "ABC01") # Replace the name with actual federation server names
$LastEvents = @()
$LockEvents = @()

#collect events from all federation servers
ForEach($Server in $FServers){
$Events = Get-WinEvent -Computername $Server -FilterHashtable @{LogName='Security';ID=411; StartTime=$((get-date).addhours(-($Period)))}| ?{$_.message -like "*Locked*"} | Select-object MachineName, TimeCreated, Message
$LastEvents += $Events

ForEach($Ev in $LastEvents){
$Array = $Ev.Message.split("`n")
$a = $Array[13].split("@")
$Temp = New-Object -TypeName PSObject -Property @{
FederationServer = $Ev.MachineName
TimeWritten = $Ev.TimeCreated
IPAddress = [string]$Array[10]
UserAccount = $a[0]
$LockEvents += $Temp

# Find unique users from the lockouts
$LockedAccounts = $LockEvents.UserAccount | sort-object -unique
write-host "A total $($LockedAccounts.count) Accounts locked out in last $($Period) hours: `n$($LockedAccounts -join ",")" -foregroundcolor RED

$Logfile = "c:\temp\Lockouts_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv"
$LockEvents | Select-object FederationServer, TimeWritten, UserAccount, IPAddress | Export-csv -nti $Logfile

PowerShell: DNS Inventory – All records

Here goes the PowerShell script for DNS Inventory for AD Integrated DNS zones. This script needs to be altered to set variable for DC Name and once ran, it would fetch details from each zone and would save it in separate files.

Updated the code to provide option to dynamic selection of DNS Server. Thanks for input from @singhalyogi22

Note: Skipping SOA records etc to avoid complexity

Continue reading “PowerShell: DNS Inventory – All records”

PowerShell: DHCP settings, leases and reservations inventory

Another day, another PowerShell script.. this time to get DHCP settings, leases and reservations from all DHCP Servers in current domain. Pre-requisite is all DHCP Servers should be Windows 2012 based and WinRM should be enabled on all of them.

Continue reading “PowerShell: DHCP settings, leases and reservations inventory”