My Powershell Script Does Not Write Output to Logfile - powershell

My Powershell script works well enough for what I want to achieve. It polls the entered in name and places that user's groups in an array. That array loops until it deletes all listed groups. Domain Users is the only group left, but that is polled by "Get-ADPrincipalGroupMembership," not "Get-ADUser." "Domain Users" is the group the user's email ties into. Once all groups are removed from their account, they are permanently disabled, but they can still access their email for paystub information until we delete their account entirely.
That said, I'm unable to write the script's group removal output to a logfile. Ideally, this will be a .log file, but a .csv file fails as well. What am I missing? The script successfully runs without error, but nothing writes to the log file of my choice.
Here is my script:
#Requires -Module ActiveDirectory
Import-Module ActiveDirectory
function Disable-ADUser{
$msg = 'Do you want to remove a user from all Security groups? [Y/N]'
do {
$response = Read-Host -Prompt $msg
if ($response -eq 'y') { # Beginning of if statment
#Asks user via a text prompt to ender the firstname and lastname of the end user to remove
$firstName = Read-Host "Please provide the First name of the User"
$lastName = Read-Host "Please provide the Last name of the User"
#The uesr's samaccoutname is found by searching exactly for the user's first name and lastname given in the above prompts
$samName = Get-ADUser -Filter "GivenName -eq '$firstName' -and Surname -eq '$lastName'"| Select-Object -ExpandProperty 'SamAccountName'
#All of the user's groups are queried based on their sam name
$listGroups = Get-ADUser -Identity $samName -Properties MemberOf | Select-Object -ExpandProperty MemberOf
#All of the user's groups are placed in an array
[System.Collections.ArrayList]$groupsArray = #($listGroups)
#Every group in the groupsArray is cycled through
foreach ($group in $groupsArray) {
#A text output is displayed before the user is removed from each group listed in the above array
#Once all groups have been cycled through, the for loop stops looping
Start-Transcript -Path Y:\Scripts\remove_user_groups.log
Write-Host "Removing $samName " -f green -NoNewline; Write-Host "from $group" -f red
Remove-ADGroupMember -Identity $group -Members $samName
Stop-Transcript
}
} # End of if statement
} until ($response -eq 'n')
}
Disable-ADUser

Here is the solution that worked.
Write-Host "Removing $samName " -f green -NoNewline
Write-Host "from $group" -f red
$OutputLine="Removing $samName from $group"
Out-File -FilePath Y:\Scripts\remove_user_groups.log -InputObject $OutputLine -Append
Remove-ADGroupMember -Identity $group -Members $samName

Windows Server 2012 is most probably using Powershell 4. Start-Transcript ignores Write-Host in powershell Versions before 5. Use Write-Output instead.
You might aswell use the -Verbose parameter for Remove-ADGroupMember.

If you are trying to write the output of the cmdlet to a file, you can do this...
#Every group in the groupsArray is cycled through
foreach ($group in $groupsArray) {
#A text output is displayed before the user is removed from each group listed in the above array
#Once all groups have been cycled through, the for loop stops looping
Write-Host "Removing $samName " -f green -NoNewline; Write-Host "from $group" -f red
Remove-ADGroupMember -Identity $group -Members $samName |
Out-File -FilePath 'Y:\Scripts\remove_user_groups.log' -Append
}
Also, there is no real reason to have this on the same line:
Write-Host "Removing $samName " -f green -NoNewline; Write-Host "from $group" -f red
... as this is not a one-liner. It is just all code on one line.
This is more prudent:
Write-Host 'Removing $samName ' -f green -NoNewline
Write-Host 'from $group' -f red
... and that screen output would still be on the same line. As a best practice. Use single quotes for simple strings, double for expansion, and some formatting use cases.

Related

Trying to remove a Computer Object from AD security group using powershell & importing from a CSV file

having a heck of a time with this. Also, wanted to preface this with I'm not the best at PowerShell as I'm just starting out. I have a CSV file that I'm trying to read the first column which happens to be 'AssetName'. These are AD joined computers.
#Get Computer
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object AssetName
$Group = "Sec Group Name"
# Set the ErrorActionPreference to SilentlyContinue, because the -ErrorAction
# option doesn't work with Get-ADComputer or Get-ADGroup.
$ErrorActionPreference = "SilentlyContinue"
# Get the computer and group from AD to make sure they are valid.
$ComputerObject = Get-ADComputer $Computer
$GroupObject = Get-ADGroup $Group
Foreach ($Computer in $Computers){
if ($GroupObject) {
# If both the computer and the group exist, remove the computer from
# the group.
Remove-ADGroupMember $Group `
-Members (Get-ADComputer $Computer).DistinguishedName -Confirm:$False
Write-Host " "
Write-Host "The computer, ""$Computer"", has been removed from the group, ""$Group""." `
-ForegroundColor Yellow
Write-Host " "
}
else {
Write-Host " "
Write-Host "I could not find the group, ""$Group"", in Active Directory." `
-ForegroundColor Red
Write-Host " "
}
}
else {
Write-Host " "
Write-Host "I could not find the computer, $Computer, in Active Directory." `
-ForegroundColor Red
Write-Host " "
}
Upon doing so, I want to remove that Asset from a specific security group. Whenever I run my script, I get this error. I don't know why it's reading it with the "#{AssetName=CompName}".
The computer, "#{AssetName=CompName}", has been removed from the group, "Sec Group Name".
Any help would be much appreciated.
With your first line you are saving a list of PSObjects to $Computers.
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object AssetName
These Objects look like this #{AssetName=Computername}
When you iterate these objects you need to specify that you only want the value of the AssetName parameter
Get-ADComputer $Computer.AssetName
Another (in my opinion better) way would be to stop using Select-Object (which storing the returned objects in $computers) and only storing a list of AssetNames in $computers like this:
$Computers = (Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv').AssetName
EDIT:
You can also use -ExpandProperty with your Select-Object:
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object -ExpandProperty AssetName

Powershell script to remove users from distribution groups

I'm trying to find a solution to remove a user from all the distribution groups they are in. I found this script but am running into issues:
$email = Read-Host "Please provide a user's email address to remove from all distribution groups"
$mailbox = Get-Mailbox -Identity $email
$DN=$mailbox.DistinguishedName
$Filter = "Members -like ""$DN"""
$DistributionGroupsList = Get-DistributionGroup -ResultSize Unlimited -Filter $Filter
Write-host `n
Write-host "Listing all Distribution Groups:"
Write-host `n
$DistributionGroupsList | ft
$answer = Read-Host "Would you like to proceed and remove $email from all distribution groups ( y / n )?"
While ("y","n" -notcontains $answer) {
$answer = Read-Host "Would you like to proceed and remove $email from all distribution groups ( y / n )?"
}
If ($answer -eq 'y') {
ForEach ($item in $DistributionGroupsList) {
Remove-DistributionGroupMember -Identity $item.DisplayName –Member $email –BypassSecurityGroupManagerCheck -Confirm:$false
}
Write-host `n
Write-host "Successfully removed"
Remove-Variable * -ErrorAction SilentlyContinue
}
Else
{
Remove-Variable * -ErrorAction SilentlyContinue
}
It will get to the stage where it lists all the groups a user is in and asks whether or not to remove them, however it seems to get stuck on the –BypassSecurityGroupManagerCheck advising that there is an issue with this parameter.
I found this article on Microsoft TechCenter forum... obviously you'll need to expand it to meet your needs. - https://social.technet.microsoft.com/Forums/exchange/en-US/99c1f07b-12fa-4e06-95bd-246a757bb00f/powershellscript-to-remove-all-group-memberships-for-one-user
$DGs= Get-DistributionGroup | where { (Get-DistributionGroupMember $_ | foreach {$_.PrimarySmtpAddress}) -contains "user#domain.com"}
foreach( $dg in $DGs){
Remove-DistributionGroupMember $dg -Member user#domain.com
}
Based on the comments on the fact the - are not the same you could just open the script in PowerShell ISE or VisualStudio Code and then next to each command as you write it out you can press Tab on your keyboard and it would correct would if the parameters work with that command. But yes I would say based on your last comment
The operation couldn't be performed because object 'rebecca.edge#greensqaureaccord.co.uk' couldn't be found on 'DCWESTBROM01.accord.local'. + CategoryInfo : NotSpecified: (:) [Get-Mailbox], ManagementObjectNotFoundException + FullyQualifiedErrorId : [Server=EXCH2016-01,RequestId=ae3c7f93-e204-4245-aa5f-8678ff68aa63,[FailureCategory=Cmdlet-ManagementObjectNotFoundException] 4EA0476A,Microsoft.Exchange.Management.RecipientTasks.GetMailbox
That the email doesn't exist. I tested the script replacing all the dashes with correct ones (best done in Notepad to avoid formatting) and seems to work without error for me.

Issue with replacing a value of cell with returned variable - Incorrect formatting?

I'm currently struggling with some of the bits and pieces in my code.
I've got a SQL query that runs within powershell, that works fine, and dumps data into CSV - that's all working good.
Then, I'm importing that data into Powershell.
$csv = Import-Csv -Path "$Path" | where {$_.Fullname -ne "System created."} #this filtering is done to sort incosistent data.
And then I'm actually struggling, as I am carrying out some checks, and would like to replace a user's PIN number with a Manager's ID number.
Write-Host "initializing loop to go through each element"
foreach ($row in $csv)
{
if(($row.primarypin.length -lt 1) -or ($row.primarypin.length -gt 5) -or ($row.primarypin -contains 'T') -or ($row.primarypin -contains 'X'))
{
Write-Host "Doing Primary Check" -BackgroundColor Green
Write-Host $row.primarypin.Length
Write-Host $row.FullName
try{
$getTempUserUsername = $row.name
Write-Host "Replacing UserID with their Manager" -ForegroundColor Magenta -BackgroundColor Cyan
$getUsersManager = Get-ADUser -identity $getTempUserUsername -Properties department, Manager | Select-Object Manager
Write-Host "Replacing with String $getUsersManager"
$row.primarypin = $null
$row.primarypin -replace $null, $getUsersManager
}
catch{
Write-Host "Replacing With a Cannot find User in AD" -ForegroundColor Yellow -BackgroundColor Red
$row.primarypin -replace "Cannnot find user, this is a system user."
}
}
else
{
Write-Host "Went directly to the else loop" -ForegroundColor red -BackgroundColor Yellow
foreach($dataset in $row)
{
Write-Host "$dataset"
}
}
}
The issue here is that whenever i'm trying to do a replace, It does seem to replace it within console, however doesn't seem to be saving it correctly to CSV file (which i'm then mailing, but already got that part correctly).
Remove-Item $path
$csv | Export-Csv -Path $path -NoTypeInformation -NoClobber
So everytime I return the dataset It comes back as
"#{name=adnameexample; FullName=Lastname, Firstname; ColourCost=0.21600; Colour Pages Printed=13; primarypin=; Office=London; Department Number=}"
In this scenario I'd love for the primarypin to be returned as the manager's ID.
(I'm also trying to use try/catch in case there is some issue".
Has anyone got some idea on how to replace the data within that particular cell?
Please help guide me, wizards!
First, this command returns an object with a manager property
$getUsersManager = Get-ADUser -identity $getTempUserUsername -Properties department, Manager | Select-Object Manager
It seems you want just the value so add -Expandproperty to the Select-Object command. Just making sure you know, it will be a distinguished name. If you want just the name you'd need to look the manager up or extract it with regex. Also, why pull the department property if it's not being used? The new line can look like
$getUsersManager = Get-ADUser -identity $getTempUserUsername -Properties Manager | Select-Object -Expandproperty Manager
To just extract the name from the distinguished name here is one way.
$getUsersManager = $getUsersManager -replace '^.*?=(.*?),[a-z]{2}=.*$','$1'
Next, when you do the replace you also need to set it back to the primary pin. I would remove the set to $null and just replace whatever is in there, even if it's nothing, with the manager.
$row.primarypin = $row.primarypin -replace '.+', $getUsersManager

PowerShell Active Directory - Compare Get-ADPrincipalGroupMembership to array of group names

I've written a script that our provisioning team uses to manage terminated users and group memberships.
The script works well, but it takes a long time due to how I chose to enumerate group memberships to begin with.
The current script uses Get-ADGroup $group -pr Members | select -ExpandProperty Members to enumerate membership.
I figure there has to be a more efficient method of doing this, so I wrote this:
$grpMemberships = Get-ADPrincipalGroupMembership $user | select name
foreach ($Group in $Groups){
if ($grpMemberships -contains $group) {
Write-Host "$grpMembership found!"
Remove-ADGroupMember $grpMembership -members $user
Write-Host "Removing $user from $grpMembership"
"$user is a member of $group" | out-file -filepath Termed_Users-$get-date -f yyy-MM-dd.txt -Append
Write-Host ""
}
}
The problem is that this never seems to execute. Any idea why this doesn't work?
Thank you #Olaf
I figured it out with your assistance.
foreach ($grpMembership in $grpMemberships.name) {
foreach ($Group in $Groups){
if ($Groups -contains $grpMembership) {
Write-Host "$grpMembership found!"
#Remove-ADGroupMember $grpMembership -members $user
Write-Host "Removing $user from $grpMembership"
"$user is a member of $group" | out-file -filepath Termed_Users-$(get-date -f yyy-MM-dd).txt -Append
Write-Host ""
}
}
}

Powershell - Order of script

I am trying to input a user using a variable and check active directory to confirm the full name of the user and pause the script before running the next command.
The script is running the pause command before the get-aduser command - see below script
#Enter Username
$username = read-host "Username"
Get-ADUser -Filter "Name -eq '$username'" | Select-Object name, samaccountname
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
#Removes user from groups
Get-ADPrincipalGroupMembership -Identity $username | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $uSername -MemberOf $_ -Confirm:$false}
write-output End
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
In my experience, Get-ADUser and similar commands can take a long time to run, possible up to 20 seconds or so. Rarely, I have found that it makes the code unusable due to some commands running before or after it. If you want to test to see if this is really the case for you, add this line in between every other line in your code:
Read-Host -Prompt "Press Enter to continue"
That way, you can test whether there is a real difference between when you put that line there, and if you don't. If there is actually a difference, you may have to look into start-sleep or wait.
I would do something like this to have the user validate, cause i think that is what you are after, before continuing to revoke the users group membership
Write-Host "`nEnter the UserName: " -NoNewline -ForegroundColor Yellow
$UserName = Read-Host
$UserName = Get-ADUser -Filter "Name -eq '$UserName'" | Select-Object Name, SamAccountName
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
While ("y","yes","n","no" -notcontains $Confirmation) {
Write-Host "`nNot a valid input! Please try again ..." -ForegroundColor Red
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
}
If ($Confirmation -eq "n" -or $Confirmation -eq "no") {
Write-Host "Aborted!" -ForegroundColor Red
Break
}
# Next step here!
# Get-ADPrincipalGroupMembership -Identity $UserName | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $UserName -MemberOf $_ -Confirm:$false}
Just another piece of code, these kind of changes needs some proper logging and error handling, while my code only logs to the console it can still be useful.
It uses confirm in place of 'pause' so the user can choose to continue or stop.
### CmdletBinding
# Alows the use of -Whatif(not used), -Confirm, -Verbose and -Debug.
# Reference: https://technet.microsoft.com/en-us/library/ff677563.aspx
# https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_functions_cmdletbindingattribute
# https://blogs.technet.microsoft.com/poshchap/2014/10/24/scripting-tips-and-tricks-cmdletbinding/
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact=’High’
)]
# Script parameters.
Param(
[parameter(HelpMessage = "Command parram, not used.")]$Command = "nothing"
#Run with PowerShell Fix, reference: https://social.technet.microsoft.com/Forums/office/en-US/fe7fb473-7ed6-4397-9c95-120201c34847/problems-with-powershell-30?forum=winserverpowershell
)
#Console clean-up.
Clear-Host
# Set error action to Stop, if something happens and it isnt inside a trap (try/catch) then stop.
$ErrorActionPreference = "Stop"
# Controls the Verbose Output
$VerbosePreference = "Continue" #Optional
#Intial message for User execution, whitespace is for the progressbars.
"
Script: Remove-ADUserGroupMembership.ps1
"
Write-Verbose "Starting main loop."
While ($true){
#White space for in between questions.
Write-Host "
"
#Retrieve username from user input.
Write-Host "Provide the ADUser for ADGroup removal here:"
$Username = read-host "Username"
#Retrieve ADUser object from AD.
Write-Verbose "Querying Active Directory for user $Username"
Try {
$ADUser = Get-ADUser $Username
Write-Verbose "User Found, $($ADUser.Name) "
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
Write-Warning "Could not find user $Username in Active Directory, check spelling and try again."
Continue #this wil reset the while loop
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve user $Username from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Retrieve GroupMembership for user.
Write-Verbose "Querying Active Directory for GroupMembership of User $($ADUser.name), exluding Domain Users"
Try {
$GroupMembership = $ADUser | Get-ADPrincipalGroupMembership | where {$_.Name -notlike "Domain Users"}
Write-Verbose "Found $($GroupMembership.count) GroupMemberships for User $($ADUser.name) (Not inluding Domain Users)"
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve GroupMembership for user $($ADUser.Name) from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Remove GroupMembership for user.
if ($pscmdlet.ShouldProcess("$($ADUser.name)", "Remove-ADPrincipalGroupMembership {$($GroupMembership.count) Groups}")) {
Write-Verbose "Entering GroupMembership removal loop for user $($ADUser.name)"
Foreach ($Group in $GroupMembership) {
Try {
$ADUser | Remove-ADPrincipalGroupMembership -MemberOf $Group -WhatIf -Confirm:$true
Write-Verbose "$Group removed from from user $($ADUser.name)"
}
catch {
Write-Warning "An Error occured, could not remove group $Group from user $($ADUser.Name)"
Continue #this will skip this group.
}
}
}
else {
Write-Warning "Action Remove-ADPrincipalGroupMembership {$($GroupMembers.count) Groups} canceled for $($ADUser.name)"
}
Read-Host "Press Enter to exit."
break #exit from while loop
}