Trouble running loop with variable - powershell

I am writing a script to pull a list of users and then archive that list of users mailboxes and archives into psts
$Lines = Get-ADUser -Filter * -Properties EmailAddress
$Lines | select EmailAddress | Where {$_.EmailAddress -ne $null} | Out-File "C:\T2\EMailAddresses.txt"
(Get-Content "C:\T2\EMailAddresses.txt") | ForEach { $_.Trim() } | Set-Content "C:\T2\EMailAddresses.txt"
$Names = Get-Content "C:\T2\EMailAddresses.txt"
$Emails = $Names.Split("#")[0]
Add-PSSnapIn -Name Microsoft.Exchange, Microsoft.Windows.AD
$SubPath1 = "\\qnap1\archive\exchange_Migration\"
$SubPath2 = "_Primary_mailbox.pst"
$SubPath3 = "_Archive_mailbox.pst"
ForEach ($Email in $Emails) { New-MailboxExportRequest -Mailbox $Email -FilePath "$Subpath1 + $Email.Split("#")[0] + $SubPath2" ; New-MailboxExportRequest -Mailbox $Email.Split("#")[0] -FilePath "$Subpath1 + $Email + $SubPath3" -IsArchive }
Where I am strugglng is getting the variables correct. I want to use the full e-mail address with the export request command but only the name part of the email (not domain) in the path for the export

I think there's a few things going on. When you are unrolling the .Split("#") on the $Names array you are then indexing into it with [0]. That's going to return the first element of the array that resulted from the split. NOT the first element resulting from each split...
But there's not a need for that anyhow. You could simply a few ways, one example:
Add-PSSnapIn -Name Microsoft.Exchange, Microsoft.Windows.AD
$Lines = Get-ADUser -Filter "EmailAddress -like '*'" -Properties EmailAddress
$Lines |
Select-Object -ExpandProperty EmailAddress |
Out-File "C:\T2\EMailAddresses.txt"
(Get-Content "C:\T2\EMailAddresses.txt").Trim() |
Set-Content "C:\T2\EMailAddresses.txt"
$Emails = Get-Content "C:\T2\EMailAddresses.txt"
$SubPath1 = "\\qnap1\archive\exchange_Migration\"
$SubPath2 = "_Primary_mailbox.pst"
$SubPath3 = "_Archive_mailbox.pst"
ForEach ($Email in $Emails)
{
$Alias = $Email.Split("#")[0]
$FilePathPrimary = "$Subpath1" + $Alias + $SubPath2
$FilePathArchive = "$Subpath1" + $Alias + $SubPath3
New-MailboxExportRequest -Mailbox $Email -FilePath $FilePathPrimary
New-MailboxExportRequest -Mailbox $Email -FilePath $FilePathArchive -IsArchive
}
Note: I obviously can't test this.
You can use emails directly, then simply calculate the arguments for your export commands on each loop iteration. This comes out cleaner and easier to read.
Also Notice:
Use of Select-Object -ExpandProperty this will return just the email addresses for the file.
Unrolling the .Trim() method on the results of Get-Content instead of running it through a ForEach-Object loop.
Moving filter criteria left in the command/pipeline by leveraging the -Filter parameter of Get-ADUser, which is a performance best practice. Especially when commands have their own and/or adequate filtering capabilities to do so.
An Aside:
There are a few other things that can be done to simplify. However, the way this is presented thus far, there's actually no need to intermediate the data with files. So I'm going to skip making enhancements in that area. Instead you could take the output from Get-ADUser and use it directly. Again, lot's of ways to do it, but building off what we already have, here's one example:
Add-PSSnapIn -Name Microsoft.Exchange, Microsoft.Windows.AD
$SubPath1 = "\\qnap1\archive\exchange_Migration\"
$SubPath2 = "_Primary_mailbox.pst"
$SubPath3 = "_Archive_mailbox.pst"
$Emails = (Get-ADUser -Filter "EmailAddress -like '*'" -Properties EmailAddress).EMailAddresses
ForEach($Email in $Emails)
{
$Alias = $Email.Split("#")[0]
$FilePathPrimary = "$Subpath1" + $Alias + $SubPath2
$FilePathArchive = "$Subpath1" + $Alias + $SubPath3
New-MailboxExportRequest -Mailbox $Email -FilePath $FilePathPrimary
New-MailboxExportRequest -Mailbox $Email -FilePath $FilePathArchive -IsArchive
}
Warning:
Beyond the scope of the question, but all the code so far assumes the mailbox indeed has an enabled archive. If or when that is not the case the attempt to export the archive will likely throw up a bloody red error. You can handle that by introducing some error handling. Or, you might consider rewriting by using Get-Mailbox from the Exchange tools instead of Get-ADUser

Related

Get specific info on domain computers from AD OUs

I'm trying to Get the Name, Manufacturer, and model of computers so i can distinguish what computers are out of warranty in AD.
I'm trying to do this by getting the computer names and putting there info into the corresponding .csv file but this fails and puts 1 ou to multiple .csv files and then moves to the second ou and does the same thing?
$myMultiArray = #(("OU=Domain Controllers,DC=FABRIKAM,DC=COM"),
("OU=Computers,DC=FABRIKAM,DC=COM"))
$myFileArray = #(("‪D:\VS-Code\Powershell\AD_Computer_Management\OUs\Domain
Controllers.csv"),("‪D:\VS-
Code\Powershell\AD_Computer_Management\OUs\Computers.csv"))
foreach ($MultiOU in $myMultiArray) {
Get-ADComputer -Filter * -SearchBase $MultiOU -SearchScope 2 | Select-object Name | Out-File -FilePath "D:\VS-Code\Powershell\AD_Computer_Management\OUs\garbage.csv"
For ($i = 0; $i – $myFileArray.Length - 1; $i++) {
Write-Host $myMultiArray[$i]
[string[]]$cnArray = Get-Content -Path 'D:\VS-Code\Powershell\AD_Computer_Management\OUs\garbage.csv'
Write-Host $OU
if ($i -eq $i) {
foreach($CN in $cnArray){
Get-WmiObject -Class:Win32_ComputerSystem -ComputerName $OU | Format-List -Property Name, Manufacturer, Model | Out-File -FilePath $myFileArray[$1]
}
}
}
}
I've tried multiple variations of different loops and if statements.
I think there are two things:
Out-File -FilePath $myFileArray[$1]
Should be:
Out-File -FilePath $myFileArray[$i]
And also you might need to append:
Out-File -FilePath $myFileArray[$i] -Append
There are a couple of things wrong in your code, like $i – $myFileArray.Length, which should be $i –lt $myFileArray.Length.
Then there is Out-File -FilePath $myFileArray[$1] as Bernard Moeskops already mentioned.
Also your code seems to want to create both the Domain Controllers.csv aswell as the Computers.csv files regardless of the OU you are currently in.
Lastly, you are using Out-File to create the CSV files where for proper CSV output, you should use the Export-Csv cmdlet.
The following code should do what you want:
$myOUArray = "OU=Domain Controllers,DC=FABRIKAM,DC=COM", "OU=Computers,DC=FABRIKAM,DC=COM"
$myFilePath = "‪D:\VS-Code\Powershell\AD_Computer_Management\OUs" # just the path for the output files is needed
foreach ($OU in $myOUArray) {
# determine the file name from the OU we're in
$fileName = if ($OU -match 'OU=Domain Controllers') { 'Domain Controllers.csv' } else { 'Computers.csv'}
$filePath = Join-Path -Path $myFilePath -ChildPath $fileName
Write-Host "Getting computer info from OU '$OU'"
# get a string array of the computernames found in the OU
$computers = Get-ADComputer -Filter * -SearchBase $OU -SearchScope Subtree | Select-Object -ExpandProperty Name
# loop through this array to get the properties you want for
# each computer and store that as objects in the $result variable
$result = foreach($machine in $computers){
Get-WmiObject -Class:Win32_ComputerSystem -ComputerName $machine | Select-Object -Property Name, Manufacturer, Model
}
Write-Host "Creating file '$filePath'"
# save the CSV file to disk
$result | Export-Csv -Path $filePath -NoTypeInformation -Force
}

Foreach loop and writing the results to a file

I'm working on a script which iterates through all users found across a domain, grabs a few credentials and then returns them in the format of an SQL INSERT statement which I want stored in a .txt file as output.
So far I've only been able to write the last user to a file however I'm able to print out in the terminal every single user. I have a feeling that I'm overwriting the .txt output file each time I iterate through my foreach loop.
Below is my code which has been sanitised:
$users = Get-ADUser -Properties uidNumber, sAMAccountName -SearchBase' OU=LiveUsers,OU=Users,OU=MyBusiness,DC=local' -Filter *
$message = ""
Set-Content -Path C:\Desktop\UIDs\currentList.txt -Value $null # ensures file is blank
foreach ($user in $users | Select-Object -Property uidNumber, sAMAccountName){
#Search in specified OU and List above for UID and name and write to a file
$message = "INSERT INTO `DataBaseNameHere`.`currentUser` (`User_id`, `User_name`) VALUES ('" + $user.uidNumber + "', '" + $user.sAMAccountName + "');" |
Out-File -FilePath C:\Desktop\UIDs\currentList.txt
}
Get-Content -Path C:\Desktop\UIDs\currentList.txt
I've tried other variations of foreach loops, Out-File and Tee-Object so far.
Assuming that the sanitized code you provided does what you want except for leaving only a single line in the output file, you need to ensure that you have either no existing output file or that it's blank, and then you add the -append switch to the Out-File cmdlet:
$users = Get-ADUser -Properties uidNumber, sAMAccountName -SearchBase 'OU=LiveUsers,OU=Users,OU=MyBusiness,DC=local' -Filter *
Set-Content -Path C:\Desktop\UIDs\CurrentList.txt -Value $null # ensures file is blank
foreach ($user in $users | Select-Object -Property uidNumber, sAMAccountName) {
$message = "INSERT INTO `databaseNameHere`.`currentUser` (`User_id`, `User_name`) VALUES ('" + $user.uidNumber + "', '" + $user.sAMAccountName + "');"
Out-File -FilePath C:\Desktop\UIDs\currentList.txt -append # -append added to not overwrite existing content.
}
See Out-File at Microsoft Docs.
The code you posted would not write anything to a file since the loop defines $message without doing anything with it, and then calls Out-File without any input.
Something like this should do what you want:
Get-ADUser ... |
Select-Object uidNumber, sAMAccountName |
ForEach-Object { "INSERT INTO `databaseNameHere`.`currentUser` (...);" } |
Out-File -FilePath C:\Desktop\UIDs\currentList.txt
Beware though, that building INSERT statements that way is vulnerable to SQL injection and should be avoided.
So, after taking elements from a few of your answers I was able to modify my code and fix the loop. I was just using the Out-File cmdlet wrong.
$users = Get-ADUser -Properties uidNumber, sAMAccountName -SearchBase 'OU=LiveUsers,OU=Users,OU=MyBusiness,DC=myCompany,DC=local' -filter *
Clear-Content -Path C:\Desktop\UIDs\CurrentList.txt
foreach ($user in $users ){
“INSERT INTO `databasename`.`currentUser` (`User_id`, `User_name`) VALUES ('"+ $user.uidNumber + "','"+ $user.sAMAccountName +"');" |
Out-File -FilePath C:\Desktop\UIDs\CurrentList.txt -Append
}

Get list of users from file for powershell loop

I'm trying to modify this PowerShell script to allow input of users from a text or CSV file. I've pulled the script directly from Tim Rhymer.
This is the original script:
Import-Module ActiveDirectory
function Get-ADUsersLastLogon() {
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$users = Get-ADUser -Filter *
$time = 0
$exportFilePath = "c:lastLogon.csv"
$columns = "name,username,datetime"
Out-File -FilePath $exportFilePath -Force -InputObject $columns
foreach ($user in $users) {
foreach ($dc in $dcs) {
$hostname = $dc.HostName
$currentUser = Get-ADUser $user.SamAccountName | Get-ADObject -Server $hostname -Properties lastLogon
if ($currentUser.LastLogon -gt $time) {
$time = $currentUser.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
$row = $user.Name + "," + $user.SamAccountName + "," + $dt
Out-File -FilePath $exportFilePath -Append -NoClobber -InputObject $row
$time = 0
}
}
Get-ADUsersLastLogon
I'm thinking I should modify the portion of the script the sets the variable of $user and how the foreach ($user in $users) loop processes each user, but can't figure it out.
As for this...
allow input of users from a text or CSV file.
… that is all that this is...
$users = Get-ADUser -Filter *
… You just replace that with your file
$users = Import-Csv -Path $UncToUserList.csv
Yet, by your question, since this is a really common, basic PowerShell thing, I am assuming you are very new to this and would advise really ramping up on PowerShell using all the freely available learning resources on TechNet MVA, MSChannel9 and YouTube, as well as freely available eBooks, as to avoid much confusion and frustration in your learning cycles as possible.
Also, you should never run any untrusted code, or code you do not fully understand. Use only in a test environment that you can fully recover from before ever considering production runs to avoid things that can cause you to have an RPE error --- 'Resume Producing Event'.

Output department and direct reports

I am trying to create an "initial" text file that will hold a script run of all users + department + direct reports. My next step after making this file is to create another file the same way but compare it to the original to see if the department for the users ever changed. (not sure yet how to compare the department value just yet)
My current issue is that the department, even though the process is identical to another program I have made in the past, won't print it. Furthermore, when it prints my direct reports it prints only the first one with the whole extension of CN=..., OU=... etc.
I want it to print this way:
username | Department(extensionAttribute14) | Direct Reports (as a single string)
we38432 | IT-Security | cm03456: 04555a: ....etc
My original script used this code for department:
$deps = Get-Aduser -filter {name -like *} -Properties name, extensionAttribute14 | Select name, extensionAttribute14 | Export-CSV $listing -notypeinformation
and this worked. I tried the {name -like *} but that gave me errors in my current program. I know the Export-CSV makes it work but I can't use this format anymore.
for the direct reports my original was this:
foreach ($ID in $directReports){
if ($ID -ne $Null){
$directreports = get-aduser $ID
$directreports.name | Out-File $output -Append
}
This code printed line by line the direct reports but I want them all listed in the same excel cell when I send it there.
I have printed a listing of all the members in the past using ":" and it worked but it is not the case with the direct reports listing. I just get errors when I use this format from my other program:
foreach ($member in $empty.members){
$string = $member.substring(3,$member.indexof(",")-3)
$members = $members + ":" + $string
}
I hope someone can help me with my two issues.
Import-Module ActiveDirectory
$documentOld = "C:\Temp\Old_Supervisor_list_mo_yyyy.txt"
Clear-Content $documentOld
$Header = `
"User ID" <#+ "|" + `
"Department" + "|" + `
"Direct Reports"#>
$Header | Out-File $documentOld -Append
$Users = Get-AdUser -Filter * -Properties name, Enabled, Manager, extensionAttribute14 | Select Enabled, name, Manager, extensionAttribute14
foreach ($user in $Users){
if ($user.enabled –eq $true) {
$name = $user.name
$directReports = Get-ADUser -Identity $name -Properties directreports | Select -ExpandProperty directreports
$department = $user.extensionAttribute14
foreach ($ID in $directReports){
if ($ID -ne $Null){
$directreports = get-aduser $ID
# $string = $directreports + ":"
}#end if $ID
}#end foreach $ID
$listing = `
$name + "|" + $deparment + "|" + $directreports#$string
$listing | Out-File $documentOld -Append
}# end if
}# end foreach $user
Let see if we can make this a little easier and efficient.
Import-Module ActiveDirectory
$documentOld = "C:\Temp\Old_Supervisor_list_mo_yyyy.txt"
$Users = Get-AdUser -Filter * -Properties name,Enabled,Manager,extensionAttribute14 | Where-Object{$_.Enabled}
$Users | ForEach-Object{
$props = #{
Name = $_.Name
Department = $_.extensionAttribute14
DirectReports = ($_.Manager | Where-Object{$_} | ForEach-Object{Get-Aduser $_ | Select-object -ExpandProperty Name}) -join ":"
}
New-Object -TypeName psobject -Property $props
} | Select-Object Name,Department,DirectReports | Export-CSV -Delimiter "|" -NoTypeInformation -Path $documentOld
First we get all the users from your directory with Get-AdUser -Filter * taking all the properties outside the norm that we want. Since you just wanted accounts that are enabled we filter those out now with Where-Object{$_.Enabled}.
The fun part is creating the custom object array ( which is necessary for input for Export-CSV). Create a small hashtable called $props where we set the properties by their friendly names. The special one being DirectReports where we take all the users manager DN's ( Assuming they have one where is what Where-Object{$_} does by filtering out nulls/empty strings.) and use Get-Aduser to get there names. Since you could have more than one manager an array is most likely returned we use -join to ensure only a single string is given for the DirectReports property. That property collection is created for every user and it is then used to create a New-Object which is sent to the output stream.
The Select-Object that follows is just to ensure the order of columns in the CSV that is created. No need for making a CSV file with lots of Out-Files when Export-CSV and -Delimiter "|" will do the hard work for you.

Trying to disable users in AD who are not in a CSV

I've been trying to find analogs to this in the forums, but it's the logic that's tying me up - putting it all together.
I have an AD and I have a CSV of users that should be in a particular OU. I want to compare the users in the OU to the CSV, and users NOT in the CSV, I want to disable them and move them to a different OU.
I'm new to Powershell and having a bit of a rough time with this. What's getting me is the comparison and IF-Then logic...I just can't get the syntax right. I've tried a few options...this is what I have right now
Import-Module ActiveDirectory
$path = "f:\aDMGMT\"
$logpath = "f:\admgmt\logs\diable_ad_users.log"
$userfile = $path + "\files\ad_currentemployees.csv"
$location = "OU=Faculty,OU=People,DC=mydomain,DC=com"
$disabledou = "OU=disabledemployees,OU=Disabled,DC=mydomain,DC=com"
$AD_users = Get-ADUser -Filter * -SearchBase "OU=Faculty,OU=People,DC=mydomain,DC=com" | select -ExpandProperty SamAccountName
$sams = $userfile | Select-Object -ExpandProperty NameUnique #the
Compare-Object $AD_users $sams | Out-File $logpath
But the tags available are things like includeequal and excludedifferent...but not includedifferent...and how would I do it for only one side?
Help!
What you could do is pipe the results into a where clause using the SideIndicator to filter on.
Compare-Object $AD_users $sams |
Where-Object{$_.SideIndicator -eq "<="} |
Select-Object -expandproperty inputobject
Using the direction that you need, either "<=" or "=>", you would then pipe into a Select-Object to restore the input object that you were filtering on. That last part would be more important if your object was multidimensional.
I am going to try this with real data as this has only breifly tested but should work.
I re-thought my logic and came up with this. It works perfectly.
Import-Module ActiveDirectory -ErrorAction Stop
$path = "f:\aDMGMT\"
$date = Get-Date
$logdate = Get-Date -UFormat "%y%m%d"
$log = $path+"\logs\diable_ad_users_"+$logdate+".log"
$userfile = $path + "\files\ad_currentemployees.csv"
$location = "OU=employees,OU=People,DC=myorg,DC=com"
$disabledou = "OU=disabledemployees,OU=Disabled,DC=myorg,DC=com"
$AD_users = Get-ADUser -Filter * -SearchBase "OU=employees,OU=People,DC=myorg,DC=com" | select -ExpandProperty SamAccountName
$sams = Import-csv $userfile | select nameunique
ForEach ($user in $AD_users)
{
$exists = $sams.nameunique -contains $user # clean output for array w/ header vs. array without header demands .namunique
If(!$exists)
{
Get-ADUser -Identity $user | Move-ADObject -targetpath $disabledou
Disable-ADAccount -Identity $user
}
}