I have to delete users from my AD through Powershell. Powershell has to ask me who I want to delete, once I type in the username it should delete the account. Also when Powershell succesfully deleted the account or it should give me a message.
I'm kind of noob at this, but here is my code:
function aduser-remove($userremove){
Remove-ADUser -Identity $delete
if ($delete -eq $userremove){
return $true
}
else {
return $false
}
}
$delete = Read-host "Which user do you want to delete? (Type in username)."
aduser-remove $delete
if ($userremove -eq $true){
Write-Host $delete "deleted succesfully!" -ForegroundColor Green
}
elseif ($userremove -eq $false){
Write-Host "An error occured by deleting" $delete -ForegroundColor Red
}
else {
Write-Host $delete "does not exist." -ForegroundColor DarkGray
}
The result here is that Powershell does ask if I want to delete the account and it works. But Powershell keeps giving me the else message instead of the if message. Deleting the account was succesfull.
I have no idea what to now or if I'm missing something (I bet I am otherwise it would work).
I hope you guys can help me!
As commented, your code uses variables in places where they do not exist.
Also, I would recommend trying to find the user first and if you do, try and remove it inside a try/catch block, as Remove-ADUser creates no output.
Below a rewrite of your code. Please note that I have changed the name of the function to comply with the Verb-Noun naming convention in PowerShell.
function Remove-User ([string]$userremove) {
# test if we can find a user with that SamAccountName
$user = Get-ADUser -Filter "SamAccountName -eq '$userremove'" -ErrorAction SilentlyContinue
if ($user) {
try {
$user | Remove-ADUser -ErrorAction Stop -WhatIf
return $true
}
catch {
return $false
}
}
# if we get here, the user does not exist; returns $null
}
$delete = Read-host "Which user do you want to delete? (Type in username)."
# call your function and capture the result
$result = Remove-User $delete
if ($result -eq $true){
Write-Host "User $delete deleted succesfully!" -ForegroundColor Green
}
elseif ($result -eq $false){
Write-Host "An error occured while deleting user $delete" -ForegroundColor Red
}
else {
Write-Host "$delete does not exist." -ForegroundColor DarkGray
}
Note also that I have put in the -WhatIf switch. This switch ensures you will only get a message of what would happen. No user is actually deleted. Once you are satisfied the code does what you want, remove the -WhatIf switch.
Hope that helps
If you have rsat installed https://www.microsoft.com/en-us/download/details.aspx?id=45520
remove-aduser https://learn.microsoft.com/en-us/powershell/module/addsadministration/remove-aduser?view=win10-ps
Related
Hallo I got a question with my update script for my active directory
I am trying to update a user using .csv document
It’s for my understanding very simple but there is an error which I cant find out why it occurs.
My Script:
#Get CSV content
$CSVrecords = Import-Csv "C:\scripts\test.csv" -Delimiter ";"
#Create arrays for skipped and failed users
$SkippedUsers = #()
$FailedUsers = #()
#Loop trough CSV records
foreach ($CSVrecord in $CSVrecords) {
$upn = $CSVrecord.UserPrincipalName
$user = Get-ADUser -Filter "userPrincipalName -eq '$upn'"
if ($user) {
try {
$user | Set-ADUser -Department $CSVrecord.Department -Company $CSVrecord.Company -ErrorAction STOP
}
catch {
$FailedUsers += $upn
Write-Warning "$upn user found, but FAILED to update."
}
}
else {
Write-Warning "$upn not found, skipped"
$SkippedUsers += $upn
}
}
The Date that I am trying to use for the Update:
UserPrincipalName
Department
Company
test.nikola#test.local
Test
123
The Error message that I get: user found, but FAILED to update
Maybe I am blind but i cant find the error ...
As mentioned in the comments, your catch block is hiding all terminating exceptions. Since you never output or inspect the given exception, there's no way to tell what went wrong.
Change it to:
try {
$user | Set-ADUser -Department $CSVrecord.Department -Company $CSVrecord.Company -ErrorAction STOP
}
catch {
$FailedUsers += $upn
Write-Warning "$upn user found, but FAILED to update: $_"
}
Inside the catch block, $_ will refer to the exception that was caught, so at least you now get a chance to see the underlying error message.
I'm working on a script that gets executed only if X account is found, but is not working as intended the if/else statements get bypassed and the code gets executed anyways. What am i doing wrong?
$Account = "XXXX"
Get-LocalUser -name $Account
if (($Account) -eq $true) {
} else {
Write-host -foreground cyan "I found it"
}
exit
If i ran the script as is it will output the text on the console even tho "XXX" account is not present, could something like that can be done?
This should do it:
$Account = "XXXX"
$AccountObject=Get-LocalUser -name $Account -ErrorAction SilentlyContinue
if (($AccountObject)) {
Write-host -foreground cyan "I found it"
} else {
Write-host -foreground cyan "No luck"
}
The issue with the sniplet provided - the return of Get-LocalUser was not used. Instead you were using a string value which is always set therefore true - as you set it to 'XXXX' in your first line.
As Bill_Stewart explains, the reason that the else block is reached is because ($Account) -eq $true evaluates to $false unless the account name is "true".
In order to test whether Get-LocalUser succeeded or failed to retrieve the user account, you can instead inspect the $? automatic variable - it will have a value of $false only if the previous command threw an error:
$AccountName = "nonExistingUser"
# Try to fetch existing user account, don't show any errors to the user
$UserAccount = Get-LocalUser -Name $AccountName -ErrorAction SilentlyContinue
# Test if the call was successful
if($?) {
Write-Host "Found account named '$AccountName'!" -ForegroundColor Cyan
$UserAccount
} else {
Write-Host "No account named '$AccountName' was found ..." -ForegroundColor Magenta
}
I am attempting to make a script that will check a variety of settings and ask permission before changing them. I'm writing this for non-IT users, so each of the user checks is a pop out message box. I thought that this part of the code was working, but now not so much. I'm getting an error indicating the user wasn't found, but that's what I'm trying to use as an if-else trigger. Error Message
As always, thanks to all the saints out there saving my behind from this.
PS: I know its not overly secure to have the password hard coded into the script, and if it were up to me, we wouldn't do it this way. But this is how the boss wants it for now.
#User Group
If (Get-LocalGroup -Name "Koko Svc"){
Write-Host "User Group Koko Svc already exists"
}
Else{
$UserConfirm=[System.Windows.Forms.MessageBox]::Show("Would you like to create the Local Group `"Koko Svc`"?","`"Koko Svc`" Group Not Found",[System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
switch ($UserConfirm){
"Yes" {
write-host "User agreed to create `"Koko Svc`""
New-LocalGroup -Name 'Koko Svc' -Description 'KoKo Svc'
}
"No" {
write-host "User declined to create `"Koko Svc`""
break
}
"Cancel" {
write-host "User stopped Settings Check"
exit
}
}
}
#Users
If (Get-LocalUser -Name "KoKo Svc"){
Write-Host "User Koko Svc already exists"
}
Else{
$UserConfirm=[System.Windows.Forms.MessageBox]::Show("Would you like to create the User `"Koko Svc`"?","`"Koko Svc`" User Not Found",[System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
switch ($UserConfirm){
"Yes" {
write-host "User agreed to create `"Koko Svc`""
$Password1 = ConvertTo-SecureString "FooBar" -AsPlainText -Force
New-LocalUser "KoKo Svc" -Password $Password1 -FullName "KoKo Svc"
Add-LocalGroupMember -Group 'Administrators' -Member ('KoKo Svc','Administrators')
Add-LocalGroupMember -Group 'Koko Svc' -Member ('KoKo Svc','KoKo Svc')
}
"No" {
write-host "User declined to create `"Koko Svc`""
break
}
"Cancel" {
write-host "User stopped Settings Check"
exit
}
}
}
If (Get-LocalUser -Name "Valued Customer"){
Write-Host "User Valued Customer already exists"
}
Else{
$UserConfirm=[System.Windows.Forms.MessageBox]::Show("Would you like to create the User `"Valued Customer`"?","`"Valued Customer`" User Not Found",[System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
switch ($UserConfirm){
"Yes" {
write-host "User agreed to create `"Valued Customer`""
$Password2 = ConvertTo-SecureString "BarFoo" -AsPlainText -Force
New-LocalUser "Valued Customer" -Password $Password2 -FullName "Valued Customer"
Add-LocalGroupMember -Group 'Administrators' -Member ('Valued Customer','Administrators')
Add-LocalGroupMember -Group 'Koko Svc' -Member ('Valued Customer','KoKo Svc')
}
"No" {
write-host "User declined to create `"Valued Customer`""
break
}
"Cancel" {
write-host "User stopped Settings Check"
exit
}
}
}
You could change it to
If (Get-LocalUser -Name "Valued Customer" -ErrorAction SilentlyContinue){
Write-Host User Valued Customer already exists
}
Else{
write-host available
}
That way only if it produces an output it will run the if portion.
A few things.
You can't use the same name for both a group and a user, which is why you get the error
The name KoKo Svc is already in use
On commands that you expect to fail, and want to take action if it does, use try/catch blocks. You can go the -ErrorAction SilentlyContinue route, but in my opinion, that's sloppy and inelegant. You'll just be ignoring the error, instead of taking action on it. I would do something like this
try
{
Get-LocalUser -Name $Name -ErrorAction Stop
$exists = $true
Write-Host "User $Name already exists"
}
catch [Microsoft.PowerShell.Commands.UserNotFoundException]
{
$exists = $false
}
then DRY the code up by tossing it in a simple function or two, so I'm not copy pasting a big chunk every time
function Test-LocalUser([string]$Name)
{
try
{
Get-LocalUser -Name $Name -ErrorAction Stop
$exists = $true
Write-Host "User $Name already exists"
}
catch [Microsoft.PowerShell.Commands.UserNotFoundException]
{
$exists = $false
}
return $exists
}
function Test-LocalGroup([string]$Name)
{
try
{
Get-LocalGroup -Name $Name -ErrorAction Stop
$exists = $true
Write-Host "Group $Name already exists"
}
catch [Microsoft.PowerShell.Commands.GroupNotFoundException]
{
$exists = $false
}
return $exists
}
Then for your code, you can do something like this
$group = "Koko Svc group"
if(!(Test-LocalGroup -Name $group))
{
$UserConfirm=[System.Windows.Forms.MessageBox]::Show("Would you like to create the Local Group `"$group`"?","`"$group`" Group Not Found",[System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
switch ($UserConfirm)
{
"Yes"
{
write-host "User agreed to create `"$group`""
New-LocalGroup -Name $group -Description $group
}
"No"
{
write-host "User declined to create `"$group`""
break
}
"Cancel"
{
write-host "User stopped Settings Check"
exit
}
}
}
#Users
$user = "Koko Svc user"
if(!(Test-LocalUser -Name "Koko Svc"))
{
$UserConfirm=[System.Windows.Forms.MessageBox]::Show("Would you like to create the User `"$user`"?","`"$user`" User Not Found",[System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
switch ($UserConfirm)
{
"Yes"
{
write-host "User agreed to create `"$user`""
$Password1 = ConvertTo-SecureString "FooBarasdasdasdasdadad2334342342!!!" -AsPlainText -Force
New-LocalUser "$user" -Password $Password1 -FullName "$user"
Add-LocalGroupMember -Group 'Administrators' -Member ($user,'Administrators')
Add-LocalGroupMember -Group $group -Member ($user,$group)
}
"No"
{
write-host "User declined to create `"$user`""
break
}
"Cancel"
{
write-host "User stopped Settings Check"
exit
}
}
}
I would also recommend tossing the user creation portion into a function as well, to reduce all the copy-paste code. If this is something designed for other people, making it easy to maintain is a priority. By making functions out of your repeated code, you only have to make a change in one place, rather than every copy/pasted section. If you want to see an example of the user creation portion in a function, lemme know and I'll toss it in an edit.
I understand Stackoverflow isn't a write your code for you forum, absolutely, but I'm finding it very difficult to find a good example of try/catch proper usage in Powershell. I have read up on fundamentals, and understand the theoretical concept, but execution I'm struggling with.
Here is a simple script that queries Active Directory:
do {
clear
Import-Module active*
"============ WhoIs Lookup ============="
Write-Host ""
$who = Read-Host "WhoIs";
$req = Get-ADUser -Identity $who
Write-Host ''
Write-Host "$who is " -NoNewline
Write-Host $req.Name -ForegroundColor Cyan
pause
} while ($run =1)
An example error is:
Get-ADUser : Cannot find an object with
identity: '5621521' under: 'DC=dcsg,DC=com'.
At C:\Tools\CSOCTools\Who_Is\whoIs.ps1:10
char:12
+ $req = Get-ADUser -Identity $who
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFoun
d: (5621521:ADUser) [Get-ADUser], ADIde
ntityNotFoundException
+ FullyQualifiedErrorId : ActiveDirecto
ryCmdlet:Microsoft.ActiveDirectory.Mana
gement.ADIdentityNotFoundException,Micr
osoft.ActiveDirectory.Management.Comman
ds.GetADUser
How would I catch this User Not Found error?
Simple example:
try {
Get-ADUser -Identity “bleh”
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
{
Write-Warning “AD computer object not found”
}
For your case:
do {
clear
Import-Module active*
"============ WhoIs Lookup ============="
Write-Host ""
$who = Read-Host "WhoIs";
try {
$req = Get-ADUser -Identity $who
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
{
Write-Warning “AD user object not found”
Write-Host ''
Write-Host "$who is " -NoNewline
Write-Host $req.Name -ForegroundColor Cyan
}
pause
} while ($run =1)
Edit: I put the Write-Host into the catch as you're eventually trying to reference to NULL when there's no object.
I got a very good example from here. For the exception types after the Catch (where I have two of them) I just grabbed them straight from the error message you provided. I haven't tried this out many times in my experience, let me know if it works for ya!
Try
{
do {
clear
Import-Module active*
"============ WhoIs Lookup ============="
Write-Host ""
$who = Read-Host "WhoIs";
$req = Get-ADUser -Identity $who
Write-Host ''
Write-Host "$who is " -NoNewline
Write-Host $req.Name -ForegroundColor Cyan
pause
} while ($run =1)
}
Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException],[Microsoft.ActiveDirectory.Management.Commands.GetADUser]
{
# Error message here
}
I am working on a validation script for a Domain Controller. I have an account that has 4 things that I need to verify. The code that I have works but I don't want it to break if one of the conditions aren't met and that is what it is doing now. So, basically I'm needing it to check all 4 criteria, regardless if the condition is met or not.
Here is my code:
if(net user "user.dadm") {
Write-Host "[√] User.dadm was created successfully" -fore GREEN
if((((uACADA "user.dadm") -band 65536) -ne 0) -eq $true) {
Write-Host "[√] Password for user.dadm is set to never expire" -fore GREEN
if((Get-ADGroupMember -Identity "Domain Admins").Name -contains "user.dadm") {
Write-Host "[√] User.dadm was added to Domain Admins" -fore GREEN
if((((uACADA "user.dadm") -band 1048576) -ne 0) -eq $true) {
Write-Host "[√] Account Delegation Authority was removed" -fore GREEN
} else {
Write-Host "[X] Account Delegation was not removed" -fore RED
}
} else {
Write-Host "[X] User.dadm was not added to Domain Admins" -fore RED
}
} else {
Write-Host "[X] Password for user.dadm has been set to expire" -fore RED
}
} else {
Write-Host "[X] User.adam was not created" -fore RED
}
What this does is it will break to the else statement if any of the conditions aren't met but I need it to continue checking each condition.
I know that I could break this up and check each condition individually but I prefer to have it as compressed as possible as it is already going to be over a thousand lines.
I guess the REAL question is if this can be done without making 4 separate IF/ELSE statements.
I'm not sure if its a structuring issue (nested IF) or a command issue (using IF and ELSE when I should use IF and ELSEIF).
Could someone point me in the right direction? Thanks in advance.
not tested, but this may be a starting point, though you may just choose the simplicity of multiple if statements.
$user = 'user.dadm'
$info = [ordered]#{
created = "[X] $user was not created"
neverexp = "[X] Password for $user has been set to expire"
admin = "[X] $user was not added to Domain Admins"
delegation = "[X] Account Delegation for $user was not removed"
}
switch ($true) {
{net user $user} {
$info.created = "[√] $user was created successfully"
}
{((uACADA $user) -band 65536) -ne 0} {
$info.neverexp = "[√] Password for $user is set to never expire"
}
{(Get-ADGroupMember -Identity 'Domain Admins').Name -contains $user} {
$info.admin = "[√] $user was added to Domain Admins"
}
{((uACADA $user) -band 1048576) -ne 0} {
$info.delegation = "[√] Account Delegation Authority for $user was removed"
}
default {Write-Host 'all are false'}
}
$info.Keys | % {
if ($info.$_.startswith('[X]')) {
Write-Host $($info.$_) -ForegroundColor Red
} else {
Write-Host $($info.$_) -ForegroundColor Green
}
}
Yes, you can write it without if tests, e.g.
$colors = #('Red', 'Green')
$messages = #('[X] Failed -', '[√] Succeeded -')
$username = "user.adm"
$result = [bool](net user "$username")
Write-Host "$($messages[$result]) Check user account exists" -ForegroundColor $colors[$result]
$result = [bool]((uACADA "$username") -band 65536)
Write-Host "$($messages[$result]) Check password never expires" -ForegroundColor $colors[$result]
$result = [bool]((Get-ADGroupMember -Identity "Domain Admins").Name -contains "$username")
Write-Host "$($messages[$result]) Check account is a domain admin" -ForegroundColor $colors[$result]
$result = [bool]((uACADA "$username") -band 1048576)
Write-Host "$($messages[$result]) Check Delegation Authority removed" -ForegroundColor $colors[$result]
NB. that if you aren't using if/else, you need some other way to do the true/false testing. I'm casting results to [bool] and using 2-element arrays. $array[$false] casts $false -> 0 and gets element 0, $array[$true] casts $true -> 1 and gets element 1. That's how the result is turned into the appropriate colors and messages.
Another reasonable way to write this would be to move the Write-Hosts into a function.
Function Report-Result {
param($Result, $Text)
if ($Result) {
Write-Host "Success - $Text" -Fore Green
} else {
Write-Host "Failure - $text" -Fore Red
}
}
$result = [bool](...)
report-result $result "Check password never expires"
... etc.
Output looks like:
Which is okayyyy - it gets your 20 lines of code down to ~10, and has no nesting, and runs all the tests.
But it really feels like you're re-inventing PowerShell DSC ("my desired state is that accounts with .adm at the end have their passwords set to never expire") or Pester - PowerShell's test framework, ("test that all .adm accounts have passwords set to never expire").
I'm not sure that you can exactly fit Pester into your use case, but it makes me think I would change everything about the script from the way it's structured to the output messages, to make it look and feel like Pester. Particularly I want clear separation of test definitions and printed output, e.g.:
1. Here are my tests, with self-explanatory names
2. Here is a loop which runs all tests, and reports on them
and that gives me something like:
Function Validate-DCUserAccountShouldExist {
param($Username) [bool](net user "$UserName")
}
Function Validate-DCUserAccountPasswordNeverExpireShouldBeSet {
param($Username) [bool]((uACADA "$username") -band 65536)
}
Function Validate-DCUserAccountShouldBeADomainAdmin {
param($Username) [bool]((Get-ADGroupMember -Identity "Domain Admins").Name -contains "$username")
}
Function Validate-DCUserAccountShouldHaveDelegationAuthorityRemoved {
param($Username) [bool]((uACADA "$Username") -band 1048576)
}
# Search for all Validation functions and run them
$tests = (gci function: |Where Name -Like 'Validate-DCUserAccount*').Name
foreach ($test in $tests) {
$result = try {
& $test "user.adm"
} catch {
$false
}
if ($result) {
Write-Host -ForegroundColor Green "[√] $test - succeeded"
} else {
Write-Host -ForegroundColor Red "[X] $test - Failed"
}
}
Which gives an output like:
My view of my second code is that:
It's longer
more complex, less easy to follow
a lot less duplication of Write-Host
more structured, separation of test definitions and output
function names explain what the state should be, which makes the output messages work for success/failure
Output messages are neater in the code, but uglier to read in the output (but could be adjusted, e.g. add spaces when printing them)
would scale to more tests nicely, just by adding more tests