Search and find data from CSV based on Criteria - powershell

I am creating powershell script to create skype for business user account.
I am trying to find the avaialble number from CSV file which looks for Status, Business Unit and location to find correct LineUri to be used.
I am using following code which always use the first SPARE number and doesn't validate the location and business unit to find the line uri.
$path = Split-Path -parent $MyInvocation.MyCommand.Definition
$newpath = $path + "\PhoneData.csv"
$LineUri = #()
$Status = #()
$BusinessUnit = #()
$Location = #()
$csv = Import-Csv $newpath -Delimiter ","
$csv |`
ForEach-Object {
$LineUri += $_.LineUri
$Status += $_.Status
$BusinessUnit +=$_.BusinessUnit
$Location +=$_.Location
}
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
if ($Status -eq $currentStatus -And $BusinessUnit -eq $userBusinessUnit -And $Location -eq $userLocation )
{
$Where = [array]::IndexOf($Status, $currentStatus)
$AvailableURI = $LineUri[$Where]
#Write-Host "Next Uri Available: " $LineUri[$Where]
$changeWhere = [array]::IndexOf($LineUri, $AvailableURI)
#Write-Host "Next Uri Available: " $Status[$changeWhere]
Try
{
Enable-CsUser -identity sudip.sapkota -RegistrarPool "s4b-fe01.tapes.com" -SipAddressType SamAccountName -sipdomain "tapes.com"
Set-CsUser -Identity sudip.sapkota -EnterpriseVoiceEnabled $true -LineURI $AvailableURI
Grant-CsDialPlan -Identity sudip.sapkota -PolicyName 'DialPlan'
Grant-CsVoicePolicy -Identity sudip.sapkota -PolicyName 'My VoicePolicy'
Write-Host "[INFO]`t Lync Enterprise account for user sudip.sapkota has been created with sip : $AvailableURI" -ForegroundColor "Green"
"[INFO]`t Lync Enterprise account for user sudip.sapkota has been created with sip : $AvailableURI" | Out-File $log -append
$i = $changeWhere
$csv[$i].Status = 'Used'
$csv | Export-Csv -Path $newpath -NoTypeInformation
Write-Host "[INFO]`t PhoneData CSV has been updated" -ForegroundColor "Green"
"[INFO]`t PhoneData CSV has been updated" | Out-File $log -append
}
catch
{
Write-Host "[ERROR]`t Oops, something went wrong: $($_.Exception.Message)`r`n" -ForegroundColor "Red"
"[WARNING]`t Oops, something went wrong: $($_.Exception.Message)" | Out-File $log -append
}
}
else
{
Enable-CsUser -identity sudip.sapkota -RegistrarPool "s4b-fe01.tapes.net" -SipAddressType SamAccountName -sipdomain "tapes.net"
Write-Host "[INFO]`t No Spare LineURI, user has been created as PC-to-PC only" -ForegroundColor "Yellow"
"[INFO]`t No Spare LineURI, user has been created as PC-to-PC only" | Out-File $log -append
}
My CSV looks like this.
Name LineUri Status BusinessUnit Location
Test 1 tel:+61396176100;ext=6100 Spare Sales VIC
Test 2 tel:+61396176101;ext=6101 Spare Sales VIC
Test 2 tel:+61396176102;ext=6102 Used Sales NSW
Test 2 tel:+61396176103;ext=6103 Spare Support WA
Test 2 tel:+61396176104;ext=6104 Spare Support WA
Test 2 tel:+61396176105;ext=6105 Used Action VIC
Test 2 tel:+61396176106;ext=6106 Spare Suppot VIC
Test 2 tel:+61396176107;ext=6107 Spare Action VIC
Can someone help to find my mistake?
As I am manually feeding the input, the test input are
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
So I need to find the LineURI which is SPARE, whose location is WA and whose BusinessUnit is Support.
I should get tel:+61396176103;ext=6103 as LineURI

I reworked your logic a bit. I felt that you could do all of this inside the foreach loop. For clarity, this does gather the data in the loop and then leaves you with an array to work with. You could just nest your actions inside the loop directly and break out or you could keep it like this and just execute your work against the array item after.
# Array of spare numbers that meet your criteria
$firstAvailableSpare
# criteria
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
$csv |`
ForEach-Object {
$LineUri += $_.LineUri
$Status += $_.Status
$BusinessUnit +=$_.BusinessUnit
$Location +=$_.Location
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
if ( ($_.Status -eq $currentStatus) -And ($_.BusinessUnit -eq $userBusinessUnit) -And ($_.Location -eq $userLocation) ) {
$firstAvailableSpare = $_ # Assign the first
$_.Status = "Used"
continue # break the loop so we don't get additional hits
}
}
$csv | Export-Csv -Path $newpath -NoTypeInformation
$firstAvailableSpare # <-- your first available spare
$firstAvailableSpare.LineUri # <-- the URI you want
edit 1: Updated for CSV export requirement
edit 2: Changed break to continue. WARNING: For some reason this breaks the ISE's debugging functionality. Stepping will cease and future break points will be ignored. I don't know why this is but it is out of scope for your goal.

Related

PowerShell AzureAD cmdlet filter not working with variable

My PowerShell script to retrieve the last sign in date of Azure AD users doesn't work when using variables within the filter for UPN. This is a well discussed topic when it comes to AzureAD PowerShell cmdlets, with the most common answer being to wrap the variable in additional quotes. However, none of the examples are for this exact cmdlet. In any case, this does not work for me. To break things down I am now using the approach of building my filter as a variable ($filter).
If I use
$filter = "UserPrincipalName eq 'user123#domain.com'"
the Get-AzureADAuditSignInLogs cmdlet passes me a result. If I use $filter = "UserPrincipalName eq '$($row.UserPrincipalName)'", Get-AzureADAuditSignInLogs gives a $null result.
In BOTH cases, Write-Host $filter outputs the exact same string so there is no problem with retrieving the entry from the CSV file. There is something "special" about the way MS's Azure AD cmdlets implement filters.
Has anyone come across this before? Is there a way I can force my variable to be as if I "hand typed" it?
Connect-AzureAD
$Result = $null
$output = $null
$filter = $null
$csv = Import-csv -Path c:\temp\UPNs.csv
ForEach($row in $csv)
{
$filter = "UserPrincipalName eq '$($row.UserPrincipalName)'"
#$filter = "UserPrincipalName eq 'user123#domain.com'"
$filter = $filter.ToString()
Write-Host $filter
$Result = Get-AzureADAuditSignInLogs -Filter $filter -Top 1 | Select-Object CreatedDateTime, UserPrincipalName
$output = ($Result.UserPrincipalName.ToString() + "," + $Result.CreatedDateTime.ToString())
$output | Out-File C:\temp\HighwaysSignInInfo.txt -Append
Write-host $output
}
CSV file:
UserPrincipalName
user123#domain.com
user1234#domain.com
user12345#domain.com
user12356#domain.com
user1234567#domain.com
This filter works just fine:
Get-AzureADAuditSignInLogs -Filter "startsWith(userPrincipalName,'name#Contoso.com')"
Although some websites say you can use Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq 'name#Contoso.com'" it doesn't seem to work for me.
The full script (with handling for bandwidth throttling)
#GetAADUserSignIns v1.0
#This script will obtain the last sign in date for each supplied UPN via a source csv file. The biggest issue with such queries is bandwidth throttling. This is handled within the script using a simple
#Try, Catch combined with a Function that can be used to make nested calls.
#IMPORTANT: Review the below 3 options to generate a source file (Option 3 is recommended)
#1. This attempts dump the entire audit log to text file. However you are likely to be throttled before the export completes so don't bother
#Get-AzureADAuditSignInLogs -Filter -All:$true | Out-File C:\Temp\AuditLogsAll.txt
#2. To get a list of all AAD accounts exported to csv (This will capture guest accounts too, the list will be huge, therefore not recommended to run Get-AzureADAuditSignInLogs with)
#Get-AzureADUser -All $true | Export-Csv C:\Temp\AADUsers.csv -NoTypeInformation
#3. Obtain a list of on-prem AD accounts that have a valid UPN
#Get-ADUser -Filter {userPrincipalName -like "*#customdomain.com"} | Select-Object userPrincipalName | Export-Csv C:\Temp\UPNs.csv -NoTypeInformation
Connect-AzureAD
Function GetLastSignInDate {
param (
[string]$upn
)
$filter = "startsWith(userPrincipalName,'" + $upn + "')"
Try
{
$Result = Get-AzureADAuditSignInLogs -Filter $filter -Top 1 | Select-Object CreatedDateTime, UserPrincipalName
$output = ($upn + "," + $Result.CreatedDateTime.ToString())
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Green
}
Catch
{
$message = $_
if ($message -like "*Too Many Requests*")
{
Write-host "Sleeping for 10 seconds due to throttling limitations..." -ForegroundColor Yellow
sleep 10
#Nested function call to retry the entry that was throttled
GetLastSignInDate $upn
}
elseif ($message -like "*This request is throttled*")
{
Write-host "Sleeping for 10 seconds due to throttling limitations..." -ForegroundColor Yellow
sleep 10
#Nested function call to retry the entry that was throttled
GetLastSignInDate $upn
}
elseif ($message -like "*null-valued*")
{
$output = ($upn + ", Not Found")
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Gray
}
elseif ($message -like "*Invalid filter clause*")
{
$output = ($upn + ", Invalid character")
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Gray
}
elseif ($message -like "*Error reading JToken*")
{
$output = ($upn + ", Script stopped due to authentication token timeout")
Write-host $output -ForegroundColor White -BackgroundColor Red
exit
}
else
{
$output = ($upn + ",Error - " + $message.ToString().SubString(0,15))
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Red
}
}
}
$csv = $null
$Result = ""
$output = ""
$filter = ""
$i = $null
$csv = Import-csv -Path C:\temp\upns.csv
ForEach($row in $csv)
{
$upn = $row.UserPrincipalName.ToLower().Replace('`r`n','').Replace('`r','').Replace('`n','').Trim()
GetLastSignInDate $upn
}

Powershell Error Handling while processing a list

I'm trying to clean up the active directory ahead of the Skype 4 Business deployment, which requires correcting sip addressing. After doing some poking around I found other problems, including unusual emails formats, which I need to account for. I was asked to roll out the changes, but due to an error Get-CsAdUser -Identity $line.Name when the user can't be found. However this means they are taking a slot from a user who could be changed, as written at this time.
I would like it if the error occurs increase $line, do not increment $limiter, and try again with the next person on the list. I'm still reading how to use try/catch and return but I'm still unsure how to structure these to allow script to process another 25 people every run.
## Collect User Base
Get-CsAdUser -OU "OU=..." -Filter {Enabled -eq $True} | Where-Object{$_.UserAccountControl -notmatch "AccountDisabled"}| Select Name, WindowsEmailAddress, SipAddress|Export-Csv -NoTypeInformation -Append -Path c:\tmp\EmailSIP_$d.csv
$csv = Import-csv c:\tmp\EmailSIP_$d.csv
## Change 25 users
$first = $True
$limiter = 0
foreach ($line in $csv){
$goodsip = -join ("sip:", $line.WindowsEmailAddress)
$sipcheck = ($goodsip -ne $line.SipAddress) #If SIP not as expected then "proceed with change is TRUE"
$otherchecks #If no other AD fields are formatted 'incorrectly' will "proceed with change be True"
If ($emailcheck -And $otherchecks){
If ($first) {
Write-Output (-join ("Name, WindowsEmailAddress, old-SipAddress, new-SipAddress"))|Out-File -Append c:\tmp\Changed_EmailSIP_$d.txt
$first = $False}
If ($limiter -lt 25){
Write-Output (-join ($line.Name,", ", $line.WindowsEmailAddress,", ", $line.SipAddress,", ", $goodsip))|Out-File -Append c:\tmp\Changed_EmailSIP_$d.txt
#Errors Generated in following line for some $line.NAMES
Get-CsAdUser -Identity $line.Name|Set-CsUser -SipAddress $goodsip -ErrorAction Ignore -whatif
$limiter++}
If ($limiter -ge 25){break} #exit loop if 25 changes
} #end of IF Email and Sip
} #end of foreach
The error I'm getting is:
Get-CsAdUser : Management object not found for identity "person1".
At line:3 char:1
+ Get-CsAdUser -Identity $line.Name|Set-CsUser -SipAddress $goodsip -wh ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (person1:UserIdParameter) [Get-CsAdUser], ManagementException
+ FullyQualifiedErrorId : Identity,Microsoft.Rtc.Management.AD.Cmdlets.GetAdUserCmdlet
Thank you #TheIcorrigible for your comment.
The working code:
foreach ($line in $csv){
$goodsip = -join ("sip:", $line.WindowsEmailAddress)
$sipcheck = ($goodsip -ne $line.SipAddress)
$otherchecks
If ($emailcheck -And $otherchecks){
If ($limiter -lt 25){
try{
Get-CsAdUser -Identity $line.Name -ErrorAction Stop|Set-CsUser -SipAddress $goodsip -whatif
If ($first) {
Write-Output (-join ("Name, WindowsEmailAddress, old-SipAddress, new-SipAddress"))|Out-File -Append c:\tmp\Changed_EmailSIP_$d.txt
$first = $False}
Write-Output (-join ($line.Name,", ", $line.WindowsEmailAddress,", ", $line.SipAddress,", ", $goodsip))|Out-File -Append c:\tmp\Changed_EmailSIP_$d.txt
$limiter++}
catch{continue}
}
If ($limiter -ge 25){break}
}
}

Powershell script Audit Remote Desktop users logon

I found a script that logs all users of RDS servers which works great;
Link here
However I want to make it specific for 1 user, not all users.
I really don't know powershell so need some help.
Param(
[array]$ServersToQuery = (hostname),
[datetime]$StartTime = "January 1, 1970"
)
foreach ($Server in $ServersToQuery) {
$LogFilter = #{
LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
ID = 21, 23, 24, 25
StartTime = (get-date).adddays(-7)
}
$AllEntries = Get-WinEvent -FilterHashtable $LogFilter -ComputerName $Server
$AllEntries | Foreach {
$entry = [xml]$_.ToXml()
[array]$Output += New-Object PSObject -Property #{
TimeCreated = $_.TimeCreated
User = $entry.Event.UserData.EventXML.User
IPAddress = $entry.Event.UserData.EventXML.Address
EventID = $entry.Event.System.EventID
ServerName = $Server
}
}
}
$FilteredOutput += $Output | Select TimeCreated, User, ServerName, IPAddress, #{Name='Action';Expression={
if ($_.EventID -eq '21'){"logon"}
if ($_.EventID -eq '22'){"Shell start"}
if ($_.EventID -eq '23'){"logoff"}
if ($_.EventID -eq '24'){"disconnected"}
if ($_.EventID -eq '25'){"reconnection"}
}
}
$Date = (Get-Date -Format s) -replace ":", "."
$FilePath = "$env:USERPROFILE\Desktop\$Date`_RDP_Report.csv"
$FilteredOutput | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
Write-host "Writing File: $FilePath" -ForegroundColor Cyan
Write-host "Done!" -ForegroundColor Cyan
So, you say …
(I really don't know powershell so need some help.)
..., but point to a very advanced PowerShell script you want to use.
It is vital that you do not use anyone's code that you do not fully understand what it is doing from anyone. You could seriously damage / compromise your system(s) and or you entire enterprise. Please ramp up to protect yourself, your enterprise and avoid unnecessary confusion, complications, issues, errors and frustration you are going to encounter:
Follow this link
As for your query...
However I want to make it specific for 1 user, not all users.
… Though the script returns all users, you can just filter / prompt for the one user you are after, without changing anything about the authors code.
Prompt for a user by adding an additional parameter in that param block
[string]$targetUser = (Read-Host -Prompt 'Enter a username')
In that $FilteredOutput section, is where you'd use the additional $targetUser parameter, using the Where-Object cmdlet or string matching there or in the ….
$FilteredOutput | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
… section. Something like...
($FilteredOutput -match $TargetUser) | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
I do not have an environment to test this, so, I'll leave that up to you.
$FilteredOutput | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
This is all basic PowerShell 'using parameters' use case, and covered in all beginning PowerShell courses, books, websites, and built-in PowerShell help files.

powershell how to find out if a variable is part of another variable

I have pasted my code below and pulled out everything that is already working, so I only have the part that isn't working as intended.
I am trying to put the EmployeeID, from a csv, in front of the Description field in AD. I can get that part to work, but the beginning of the If statement where I try to check if the $ID is already in the description fails; it just keeps adding it every time the script runs.
I have tried making both the $ID and $Description type as string with Out-String, and I have left that out, but it's the same result. I have tried -notcontains, -notmatch, and -notlike (which I believe is the correct one to use), but none work. I have even put my variables in a text file to make sure they are pulling the correct information.
I am still learning all of the intricacies of Powershell. Can anyone see what I'm doing wrong?
# Get script Start Time (used to measure run time)
$startDTM = (Get-Date)
#Null out variables
$Users = $Null
$ID = $Null
$Users = Import-Csv .\ImportADUsers\Test-Import-user-data.csv
Import-Module ActiveDirectory
$path = Split-Path -parent ".\ImportADUsers\*.*"
#Create log date
$logdate = Get-Date -Format yyyy-MM-dd-THH.mm.ss
$logfile = $path + "\logs\$logdate.logfile.txt"
# Enumerate the users, one line at a time.
# This assumes the first line is a header line defining the fields.
ForEach ($User In $Users)
{
# Retrieve values from the csv.
$ID = $User.HRRef
# Retrieve the sAMAccountName of the user from AD.
$UserDN = (Get-ADUser -LDAPFilter "(employeeID=$ID)").sAMAccountName
$ID | Out-File $logfile -Append
$IDString = $ID | Out-String
#Retrieve the Description of the user from AD.
$Description = Get-ADUser -Identity $UserDN -Properties description
$Description = $Description.description | Out-String
$Description | Out-File $logfile -Append
# Make sure there is only one user with this employeeID.
If ($UserDN.Count -eq 1)
{
IF ($Description -notlike $IDString) {Set-ADUser -Identity $UserDN
-Description "$($ID) - $($Description)" }
}
Else {"User with ID $ID either not found, or more than one user found."
| Out-File $logfile -Append}
#Log error for users that are not in Active Directory or EmployeeID
#found more than once
}
#Finish
#The lines below calculates how long it takes to run this script
# Get End Time
$endDTM = (Get-Date)
# Echo Time elapsed
"Elapsed Time: $(($endDTM-$startDTM).totalminutes) minutes"
#Append the minutes value to the text file
"Import took $(($endDTM-$startDTM).totalminutes) minutes to complete." |
Out-File $logfile -Append
#SCRIPT ENDS
Your string comparison is incorrect. Below is how to fix it.
The change: -notlike $IDString => -notlike "*$ID*"
ForEach ($User In $Users)
{
# Retrieve values from the csv.
$ID = $User.HRRef
$ID | Out-File $logfile -Append
# Retrieve the SAMAccountName of the user from AD.
$UserDN = (Get-ADUser -LDAPFilter "(employeeID=$ID)").SAMAccountName
#Retrieve the Description of the user from AD.
$Description = (Get-ADUser -Identity $UserDN -Properties description).Description
$Description | Out-File $logfile -Append
# Make sure there is only one user with this employeeID.
If ($UserDN.Count -eq 1 -and $Description -notlike "*$IDString*")
{
Set-ADUser -Identity $UserDN -Description "$ID - $Description"
}
Else
{
"User with ID $ID either not found, or more than one user found." | Out-File $logfile -Append
}
}

Simplify powershell query

I am learning powershell, and a looking to query couple of AD groups and determine whether a user is member of AD group.
Part 1 : Query a AD group which has 10 nested AD groups
Part 2: Query user's AD group and pull a list
Part 3: Not posted, but to compare output of Part 1 and Part 2
Searched online and found some tits and bits. I am aware of Active Directory module, but avoid using it, since this script would be executed by user who is non-technical and avoid installing RSAT just for that.
I have Powershell version 2 and Windows 7
Part 1
group1 is the AD group which has 10 nested AD groups.
Write-Host "Fetching information from groups.Please wait.."
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$group=[System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity ($ct,'group1')
$group1 = $group.GetMembers | Where {$_.StructuralObjectClass -eq "group"} | Select SamAccountName
$group1 = $group1 -replace("=", " ") -replace("{", " ") -replace("#", " ") -replace ("}", " ") -replace("SamAccountname", " ") -replace '\s+', ' '
$ADGroups = foreach ($l in group1) {$l.trim()}
To ensure the AD group information is fetched by program or exit the script
if (($ADgroups | out-string) -like $null) {
Write-Host "Unable to fetch AD groups information" -foreground "red"
Start-sleep 10
break
}
Part 2
Temp location where files can be written
$location = "C:\AD"
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($ct, $username)
If ($user -like $null) {
Write-host "User does not exist in AD" -foreground "magenta"
start-sleep 10
break
}
Write-Host "Please wait...Looking user group membership..."
$usergroups = $user.GetGroups()
Removing file if exists.
Remove-Item $location\useradgroups.txt
$usergroups | select SamAccountName | Out-File $location\useradgroups.txt -append
$testr = gc $location\useradgroups.txt
if (($testr | out-string) -like $null) {
Write-Host "Unable to fetch user AD groups information" -foreground "red"
Start-sleep 10
Remove-Item $location\useradgroups.txt
break
}
$useradgroups = foreach ($l in $testr ) {$l.trim()}
$useradgroups | Select-String -Pattern "\w" | out-file $location\useradgroups.txt
$useradgroups = gc $location\useradgroups.txt
Question:
Unless I trim the output, cannot compare it. so had to write the script as shown above:
Avoid writting the output to text file
Avoid -replace("=", " ") -replace("{", " ") in Part 1
Simplify the code.
Any suggestion from Powershell gurus would be welcomed. It would help me in learning process
So what you're trying to do is recursively get ADGroupMembers? Sort of like this or this? There is always more than one way to do things. You can use the [ASDI] type accelerator.
$groups = [adsi]'LDAP://CN=GroupName,OU=Groups,OU=Place,DC=Tomorrow,DC=Today'
$group | Get-Member
foreach($group in $groups){
$members = $group.member
foreach($member in $members){
#Is this a group or a user?
#If group Get the Group members
#If a user say the user is part of the group
}
}
Are you trying to format the distinguishedName of a user?
$user = "CN=Huck Finn,OU=Users,OU=Today,OU=Tomorrow,DC=Yesterday,DC=com"
$splits = ($user -split ',')
[PSCustomObject]#{Username=$splits[0].substring(3);OUPath=($splits[1..($splits.Count-1)] | % {$_.substring(3)}) -join '\'}
Hope I helped.
Untested, but can't you use something like this for part 1?
Import-Module ActiveDirectory
Get-ADGroupMember "group1" | select sAMAccountName