I have to loop for each object in an input file, doing a Get-ADUser on each object, and I want to handle the ADIdentityNotFoundException error without stopping the loop. Is there a better way I can accomplish this (simplified for example's sake):
Import-Csv $input | Foreach-Object {
$manager = "BLANK"
if ($user = Get-ADUser $_."samaccountname" -properties * ) {
# I don't think I need this in an IF{} since the line below won't work
# so $manager will be equal to the last value set, "BLANK", but
# this makes it easier to understand what I want to happen
$manager = $user."manager"
# I need more properties (thus -properties *) but again just an example
}
}
In essence, if the Get-ADUser lookup is successful, set $manager = $user."manager"
If it is not successful, do not stop the loop, do not copy the value of the previous user, have $manager = "BLANK"(or whatever). The issue I have with the try/catch solutions, is that ADIdentityNotFoundException doesn't trigger the catch unless I add -ErrorAction Stop, which will cause the undesired result of the program terminating.
I'm not sure why your program would be terminating. Using the below sample code loops through all the users in the array. I have purposely entered an incorrect username into the second value of the array (position [1]):
$users = "username1", "username2", "username3" #username2 is purposely incorrect
foreach ($i in $users){
try{
$user = Get-ADUser -Identity $i -Properties * -ErrorAction Stop
Write-Host "Found"
}catch{
Write-Host "Not found"
}
}
my output is
found
not found
found
Related
I have a script that looks at a CSV, filters the email address and performs some tasks:
foreach($aduser in (import-csv "C:\Temp\users.csv")){
Get-ADUser -filter "emailaddress -eq '$($aduser.emailaddress)'"|
Set-ADobject -ProtectedFromAccidentalDeletion $false
Get-ADUser -filter "emailaddress -eq '$($aduser.emailaddress)'" |
Set-ADUser -Enabled $false
Get-ADUser -filter "emailaddress -eq '$($aduser.emailaddress)'" |Move-ADObject -TargetPath "OU=Sep 20,OU=Disabled User Accounts,DC=Mydomain,DC=Domain,DC=uk" -PassThru | Disable-ADAccout}
If ($Error){
$FailMailParams = #{
To = 'Example#Gmail.com'
From = 'Example#Gmail.com'
SmtpServer = 'My.smtp.server'
Subject = 'Script Errors Out'
Body = 'There was an error with the script!!'
}
Send-MailMessage #FailMailParams
} else {
$SuccessMailParams = #{
To = 'Example#Gmail.com'
From = 'Example#gmail.com'
SmtpServer = 'My.smtp.server'
Subject = 'Success'
Body = 'The script ran successfully'
}
Send-MailMessage #SuccessMailParams
}
The problem I am facing is that even if the script runs successfully I still get the error email. If I change the code to if ($error -eq 1) and the code errors out I get the successful email. I think it's the If ($error) Variable causing the issue but I don't know what to use?
Error control can be handled in different ways. In your code, you have nothing that is indicating $error that should be set to 1.
I suggest you to find some examples of how to use the Try-Catch structure, as you will have to use it a lot in Powershell.
Find an example below which should be easy to adopt for your already made code:
try
{
# Code that performs the work intended
}
catch
{
# What to do in case any error is raised from above Try section
$error = 1
}
finally
{
# Here you post code that will be executed no matter errors happened or not,
# for example your email definition and execution.
}
Said this, your code has more room for improvement, but if you are starting with it, let's not focus on that part still.
I have a list of potential account ids. I would like to use Get-ADUser to query if the account or a variation of it exists in the environment. I would then like to capture the data including which accounts ids from my original list don't have any accounts in the environment.
I have successfully captured data for account ids that have an account or a variation of the account id in the AD environment. I am having difficultly with capturing the account ids from my original list that do not produce any results using Get-ADUser
foreach ($user in $inputdata)
{$user = $user + "*"
$(try {Get-ADUser -filter {SamAccountName -like "$user"} -properties Description} catch {$null}) | % {if ($_ -ne $null) {[pscustomobject]#{"ID"=$_.SamAccountName;"DN"=$_.DistinguishedName;"Desc"=$_.Description}}
else {$noaccount += $user}
}
My pscustomobject populates properly with data from everyone that does have an account. But there are no values in $noaccount even though there are ids in my list that do not have accounts in the environment. What should I do to capture the instances which do not have accounts using Get-ADUser?
Also, no error is outputted.
The following should achieve what you want.
$noaccount = [Collections.Generic.List[String]] #()
foreach ($user in $inputdata) {
$userToCheck = Get-ADUser -Filter "SamAccountName -like '$user*'" -properties Description
if ($userToCheck) {
[pscustomobject]#{"ID"=$userToCheck.SamAccountName
"DN"=$userToCheck.DistinguishedName
"Desc"=$userToCheck.Description
}
}
else {
$noaccount.Add($user)
}
}
Explanation:
$noaccount is initialized as a generic list of strings so that we can use the .Add() method rather than the inefficient += operator.
$userToCheck will contain a found user object or $null depending on whether the query found a result. If a user is found, the if condition is $true and your custom object is output. If no user is found, the else condition is triggered and the data stored in $user is added to the $noaccount collection.
I changed the -Filter slightly to remove the script block notation because it is not a script block. The online documentation of the command teaches bad habits by demonstrating the use of script block notation. Instead the filter should be surrounded by double quotes with the values on the inside surrounded by single quotes. The double quotes will allow for PowerShell interpolation to expand variable within. The single quotes will be passed in literally so that the value is interpreted as a string by Get-ADUser.
With your attempt, the try {} block would rarely throw an error and would not throw an error just because an account was not found. You would have to remove the -Filter in favor of the -Identity parameter to produce errors when no object is found. You will still see errors if there are connectivity issues between your session and the domain server though. When your Get-ADUser command produced no output, nothing would get piped into the the foreach {} script block. Therefore, your if {} else {} would never be evaluated.
Enhancement Considerations:
Following some insight provided by Lee_Dailey, instead of adding the not found accounts to a separate collection, you could incorporate them into your custom object output. Maybe you could add a new property that states whether or not they are found. See below for an example:
$noaccount = [Collections.Generic.List[String]] #()
foreach ($user in $inputdata) {
$userToCheck = Get-ADUser -Filter "SamAccountName -like '$user*'" -properties Description
if ($userToCheck) {
[pscustomobject]#{"User" = $user
"ID"=$userToCheck.SamAccountName
"DN"=$userToCheck.DistinguishedName
"Desc"=$userToCheck.Description
"In_AD" = "Yes"
}
}
else {
[pscustomobject]#{"User" = $user
"ID"=$null
"DN"=$null
"Desc"=$null
"In_AD" = "No"
}
}
}
I'm trying to loop a list of users where it is unknown if it contains English or Swedish and in addition to this check I need to know if there is an account called "CMG*" before executing the main task.
The problem is that my first 'Try' runs the lines as expected but the second one is ignored, so it apparently exit the loop on 'continue' it seems.
Can you not have 2 sets of 'Try-Catch' inside an 'if' statement?
What I have tried is to flip the two sets of 'Try-Catch' and it results in whatever is first in the loop execute and the bottom one is ignored.
foreach($User in $Users){
if($User."ADattribute"){
try {
if(!(Get-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Calendar" | Where-Object User -like 'CMG*')){
Add-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Calendar" -User <SecretUser> -AccessRights Reviewer
}
continue
}
catch [System.Management.Automation.RemoteException] {
Write-Host ":\Calendar could not be found"
}
try {
if(!(Get-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Kalender" | Where-Object User -like 'CMG*')){
Add-MailboxFolderPermission -Identity "$($User.UserPrincipalName):\Kalender" -User <SecretUser> -AccessRights Reviewer
}
continue
}
catch [System.Management.Automation.RemoteException] {
Write-Host ":\Kalender could not be found."
}
}
}
I think you are missing the function of the keyword continue. When executing the continue keyword, the current iteration stops and it continues with the next iteration. So I think if you remove the continue keyword(s) that your script will work as excepted.
https://devblogs.microsoft.com/scripting/powershell-looping-the-continue-statement/
So here's what I'm attempting to do:
I manually input a name, and then I want to get a list of users who work under the person whose name I input (extensionattribute9 is who the user works under). However, for each person that works under that person, I also want to run the process for them, and see if anyone works under them as well. I want this process to continue until no one works under the current user.
I've managed do to this up to 3 times without a while loop, but as I don't know how deep I would have to go to get everyone, I feel using a while loop would be better overall, especially in terms of code length.
Here is the code I currently have:
$users = Get-ADUser -Filter * -Properties extensionattribute9,Displayname,mail,title
$users | ForEach-Object {
if ($_.extensionattribute9 -like '*Lynn, *')
{
$_ | select Displayname,userprincipalname,title,extensionattribute9
$name = $_.Displayname
while ($_.extensionattribute9 -ne $null){ $users | ForEach-Object {
if ($_.extensionattribute9 -eq $name)
{
$_ | select Displayname,userprincipalname,title,extensionattribute9
$name=$_.Displayname
}
}
}
}
}
When I run the code I get a user (User A) under 'Lynn', and then a user under User A. After that, nothing. The program still continues to run, but nothing gets returned. I'm guessing it's stuck in an infinite cycle, but I don't know where better to put the while loop. Help?
It sounds like you are trying to do a recursive search with nested while/for-each loops which can end badly. You can try something like this:
Function Get-Manager {
param([Object]$User)
$ManagerName = $User.extensionattribute9
# Recursion base case
if ($ManagerName -eq $null){
return
}
# Do what you want with the user here
$User | select Displayname, userprincipalname, title, extensionattribute9
# Recursive call to find manager's manager
$Manager = Get-ADUser -Filter "Name -like $ManagerName"
Get-Manager $Manager
}
# Loop through all ADusers as you did before
$Users = Get-ADUser -Filter * -Properties extensionattribute9,Displayname,mail,title
Foreach ($User in $Users) {
Get-Manager $User
}
Please note I don't have experience using Powershell with Active Directory so the syntax may be incorrect, but shouldn't be hard to fix. Hope this helps!
I'm not familiar with Powershell, but one possible reason you're having trouble is that $_ is being used to mean two different things, depending on whether you use it inside the while loop or not. Is Powershell really smart enough to know what you mean?
More important: the code
$_ | select Displayname,userprincipalname,title,extensionattribute9
$name = $_.Displayname
appears in two places close together. This is a definite code smell. It should appear once and only once.
When you're traversing a hierarchy and you don't know how deep it will go, you must use a recursive algorithm (a function that calls itself). Here it is in pseudocode:
function myFunc ($node) {
//report the name of this node
echo "node $node found." ;
// check to see if this node has any child nodes
array $children = getChildNodes ($node) ;
if (count($children) == 0) {
//no child nodes, get out of here
return ;
}
//repeat the process for each child
foreach($children as $child) {
myFunc($child) ;
}
}
I'm learning Powershell by making a script that will hopefully automate everything that needs to be done when we get a new hire or a consultant. Currently I'm working on the part that will create the AD account. Below are the variables specific to this portion of the script.
#Variables for preliminary SamAccountName, modifiers and initials array
$PrelSamAccountName = ("se" + [string]$GivenName.Substring(0,3) + [string]$SurName.Substring(0,3)).ToLower()
$Modifier1 = 1
$Modifier2 = 0
$InitialsArray = #("x","y","z")
Here is the loop. I cut out a bunch of parameters on New-ADUser to make it less cluttered.
try {
#Checks if preliminary SamAccountName is taken or not
$ADCheck = Get-ADUser -Filter {SamAccountName -eq $PrelSamAccountName}
#Creates new user
New-ADUser -Name $Name -SamAccountName $PrelSamAccountName
} catch {
#Replaces final character in preliminary SamAccountName with "1++"
while (($ADCheck | Select-Object -ExpandProperty SamAccountName) -eq $PrelSamAccountName) {
$PrelSamAccountName = ([string]$PrelSamAccountName.Substring(0,7) + ($Modifier1++)).ToLower()
}
#Changes $Initials from $null to x/y/z if an existing user has identical name as new user
while (($ADCheck | Select-Object -ExpandProperty Name) -eq $Name) {
$Initials = $InitialsArray[$Modifier2++]
$Name = $GivenName + " " + $Initials + " " + $SurName
}
}
Everything is working as intended, except for the fact that a new user is created every other time I run the loop. Ideally I would want it to create a new user every time it is run. :)
I'm assuming it has something to do with the placement of the $ADCheck variable, but after having rewritten this portion multiple times I simply can't get it to work. Any ideas?
Thanks in advance.
You have some logical problems in here:
Follow this Approach:
Pseudocode:
while (user exist) {create new username}
new-aduser new username
PS Code:
function new-sam
{
#creates new sam
}
$sam = "username"
$a=$false
while (!$a)
{
try {
$a = [bool](Get-ADUser -Identity $sam )
}
catch {
$a = $false; $sam = new-sam
}
}
new-aduser ....