Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
beepsandboops
Jan 28, 2014
How do you share scripts with a group of people? Right now, our department just has a handful of scripts on a shared network drive that I just alias in Powershell and just call that way.

On another note, do you post your scripts publicly on GitHub or other sites to put on resumes? It sounds like a good idea, but my scripts have so much company-specific parts I'd have to scrub that I don't know if it'd be worth it.

Adbot
ADBOT LOVES YOU

Fiendish Dr. Wu
Nov 11, 2010

You done fucked up now!

beepsandboops posted:

How do you share scripts with a group of people? Right now, our department just has a handful of scripts on a shared network drive that I just alias in Powershell and just call that way.

On another note, do you post your scripts publicly on GitHub or other sites to put on resumes? It sounds like a good idea, but my scripts have so much company-specific parts I'd have to scrub that I don't know if it'd be worth it.

We're working on getting all of our scripts in github at the the moment to avoid any (more) fuckups.

I do keep a few of my (scrubbed) scripts in my personal github in a /scripts repo that I guess could work as a mini showcase of what I can do. I probably wouldn't put it on a resume but when the question comes up in an interview I could point them over to it. I also keep my profile in there and that helps a lot.

Hadlock
Nov 9, 2004

What's the generally accepted practice with scripts you wrote at work and sharing them on the internet? I wrote some (well I think, anyways) pretty impressively elegant scripts that would genericize/sanitize easily that I would like to put up on github at some point. However at least one creepy dev at my last office has found and is following me on github now and it would be pretty easily attributable to me as I was pretty prolific with introducing powershell to our workflow.

vanity slug
Jul 20, 2010

Hadlock posted:

What's the generally accepted practice with scripts you wrote at work and sharing them on the internet? I wrote some (well I think, anyways) pretty impressively elegant scripts that would genericize/sanitize easily that I would like to put up on github at some point. However at least one creepy dev at my last office has found and is following me on github now and it would be pretty easily attributable to me as I was pretty prolific with introducing powershell to our workflow.

Check your contract. Generally stuff you code at work is owned by your company.

beepsandboops
Jan 28, 2014
Been learning a lot more about functions and working with parameters for input.

Right now I'm trying to write a script that accepts input via parameters, then passes the input on to a function.

However, I think Powershell is getting tripped up b/c the parameters are named the same. Is there a good way to design around this? Exactly how rear end-backwards am I writing this?

code:
#requires -Modules ActiveDirectory

<#
    .SYNOPSIS

    Performs all offboarding tasks for departing employees 
    .DESCRIPTION

    This script does a variety of tasks for departing employees including setting AD account expiration
#>

[CmdletBinding()]

Param (
    [Parameter(Mandatory=$True,HelpMessage="Departing employee's Active Directory username")]
    [string]$username,
    
    [Parameter(Mandatory=$True,HelpMessage="Employee's departure date")]
    [DateTime]$departureDate
)

function Schedule-Departure {
    <#
    .SYNOPSIS

    Schedules another script to run at the end of the user's last day
    .DESCRIPTION

    Sets a scheduled task in Windows to run a series of tasks for a user's last day
    #>
    
    [CmdletBinding()]

    Param (
    [Parameter(Mandatory=$True,HelpMessage="Departing employee's Active Directory username")]
    [string]$username,
    
    [Parameter(Mandatory=$True,HelpMessage="Employee's departure date")]
    [DateTime]$departureDateEOD
    )

    $trigger = New-ScheduledTaskTrigger -Once -At $departureDateEOD
    $action = New-ScheduledTaskAction -Execute "C:\users\blah\Schedule Departure.ps1" -Argument "-username $username"
    $task = New-ScheduledTask -Trigger $trigger -Action $action
    Register-ScheduledTask -TaskName "Employee Departure $username" -InputObject $task -Description "Hides user from Exchange address list, archives user folder, and modifies AD groups."

}

Import-Module -Name ActiveDirectory

$departureDateEOD = $departureDate.AddHours(18)
Set-ADAccountExpiration $username -Confirm -DateTime $departureDateEOD

Schedule-Departure -username $username -departureDateEOD $departureDateEOD

PBS
Sep 21, 2015

beepsandboops posted:

Been learning a lot more about functions and working with parameters for input.

Right now I'm trying to write a script that accepts input via parameters, then passes the input on to a function.

However, I think Powershell is getting tripped up b/c the parameters are named the same. Is there a good way to design around this? Exactly how rear end-backwards am I writing this?

I'm by no means an expert, but you can prompt for user input using Read-Host instead of setting parameters twice.

I personally don't see the point in using a function here since you just end up calling it once directly in the script. (Learning I guess)

You don't seem to use $departureDate itself anywhere, so I'd just have it add the 18 hours from the get go instead of setting two different variables.

You don't need the $task variable, don't need to use New-ScheduledTask, just Register-ScheduledTask.

code:
#requires -Modules ActiveDirectory

<#
    .SYNOPSIS

    Performs all offboarding tasks for departing employees 
    .DESCRIPTION

    This script does a variety of tasks for departing employees including setting AD account expiration
#>


$username = Read-Host "Enter Username: "
[DateTime]$departureDate = Read-Host "Enter Departure Date: "


function Schedule-Departure {
    <#
    .SYNOPSIS

    Schedules another script to run at the end of the user's last day
    .DESCRIPTION

    Sets a scheduled task in Windows to run a series of tasks for a user's last day
    #>
    
    [CmdletBinding()]

    Param (
    [Parameter(Mandatory=$True,HelpMessage="Departing employee's Active Directory username")]
    [string]$username,
    
    [Parameter(Mandatory=$True,HelpMessage="Employee's departure date")]
    [DateTime]$departureDateEOD
    )

    $trigger = New-ScheduledTaskTrigger -Once -At $departureDateEOD
    $action = New-ScheduledTaskAction -Execute "C:\users\blah\Schedule Departure.ps1" -Argument "-username $username"
    Register-ScheduledTask -TaskName "Employee Departure $username" -Action $action -Trigger $trigger -Description "Hides user from Exchange address list, archives user folder, and modifies AD groups."

}

Import-Module -Name ActiveDirectory

$departureDateEOD = $departureDate.AddHours(18)
Set-ADAccountExpiration $username -Confirm -DateTime $departureDateEOD

Schedule-Departure -username $username -departureDateEOD $departureDateEOD

slightpirate
Dec 26, 2006
i am the dance commander
As an effort to clean up our horribly maintained Active Directory Users OU, I've put together a project that I know can be done in powershell, but working out the logic for it has been a challenge for me thus far. The goal of this was to use a script to pull all of the user's in an OU down to a .CSV file, make sweeping changes and corrections in excel, then populate AD with that new information.

I've gotten the first half of that to work. the CSV file generates with all of the requisite fields. Repopulating the attributes seems to be odd. I've made sure my objects and variables are set correct, and the column headers in the csv file match the variables in the script but only part of the attributes apply.

Example - when applying the address tab information, sometimes the city will change, sometimes it won't, country never changes and I'm not sure why.

Total PS newbie, so any help is appreciated.
e: tried to edit for forum viewing D: sorry if I'm breaking tables.

code:
# Import AD Module             
Import-Module ActiveDirectory            

write-Host 'Update AD Attributes' -NoNewline -ForegroundColor green            
# Import CSV into variable $users           
      
$users = Import-Csv -path "C:\powershell\Final Scripts\adusers.csv"       

#Define SamAccountName variable
$sam = $_.'SamAccountName'

foreach ($user in $users) {            
#Search in specified OU and update attributes            
Get-ADUser -Filter "SamAccountName -eq '$($user.samaccountname)'" -Properties * -SearchBase "OU=Testing,OU=oneup,DC=domain,DC=com"  | 

#Command formatting -ldapattribute $($uservariable.associatedCSVfield) 
#Set General Tab Fields
Set-ADUser -server "domaincontrollerredacted" -GivenName $($user.GivenName) -Surname $($user.Surname) -DisplayName $($user.DisplayName) 
-Description $($user.Description) -Office $($user.Office) -OfficePhone $($user.OfficePhone) -emailaddress $($user.emailaddress) |

#Set Address Tab Fields
Set-ADUser -server "domaincontrollerredacted" -StreetAddress $($user.streetAddress) -l $($user.City) -state $($user.state) 
-postalCode $($user.postalCode) -country $($user.Country) |

#Set Organization Tab Fields
Set-AdUser -server "domaincontrollerredacted"  -title $($user.Title) -department $($user.Department) -company $($user.Company) |

#Set Profile Tab Fields
Set-Aduser -server "domaincontrollerredacted" -scriptpath $($user.ScriptPath)  -homeDrive $($user.HomeDrive) -homeDirectory $($user.HomeDirectory) |
 
}

Write-Host 'done!'

Venusy
Feb 21, 2007
What format are you specifying the country in? AD wants it as a two letter code as specified by ISO 3166.

By default, Set-ADUser doesn't return any output to the pipeline if you don't use the PassThru parameter. So if you do Get-ADUser | Set-ADUser | Set-ADUser | Set-ADUser | Set-ADUser, as it looks like you're doing from the formatting there, and none of the Set-ADUsers have -PassThru specified, only the first one will actually complete, the rest have no idea which user object they would be operating on.

Rather than doing 4 calls to Set-ADUser, it might also be better to just do the one, and to use splatting (type "help about_splatting") to help make things look more manageable.
code:
foreach ($user in $users) {
    $splat = @{
        GivenName = $user.GivenName
        Surname = $user.Surname
        DisplayName = $user.DisplayName
        #snip, but repeat for all attributes - Set-ADUser parameter on left, value on right
    }
    Get-ADUser $user.SamAccountName | Set-ADUser @splat -WhatIf -Server REDACTED
}

beepsandboops
Jan 28, 2014

PBS posted:

I'm by no means an expert, but you can prompt for user input using Read-Host instead of setting parameters twice.

I personally don't see the point in using a function here since you just end up calling it once directly in the script. (Learning I guess)

You don't seem to use $departureDate itself anywhere, so I'd just have it add the 18 hours from the get go instead of setting two different variables.

You don't need the $task variable, don't need to use New-ScheduledTask, just Register-ScheduledTask.
I used to have all my scripts set up to accept input with Read-Host, but I've been learning more and it seems like the consensus is that for your code to be more extensible/modular, parameters are the way to go for input and functions are a good way to break up different ideas in code

I'm still figuring out how to put it all together, but I just got Powershell Step by Step so hopefully that will help me understand how it fits

Judge Schnoopy
Nov 2, 2005

dont even TRY it, pal
So after writing a bunch of poo poo today I learned Forms cannot be passed to remote computers regardless of pssession or invoke command. Is best practice to create a secondary powershell or batch script and open that with invoke command? Or is there a better way to get a remote computer to display a message box with an input text box that writes to a file?

Also, if a computer has a default security setting to not allow powershell execution, will invoke command fail when trying to execute it? Or will invoke command somehow use my admin credentials to bypass the security?

nielsm
Jun 1, 2009



Judge Schnoopy posted:

So after writing a bunch of poo poo today I learned Forms cannot be passed to remote computers regardless of pssession or invoke command. Is best practice to create a secondary powershell or batch script and open that with invoke command? Or is there a better way to get a remote computer to display a message box with an input text box that writes to a file?

Also, if a computer has a default security setting to not allow powershell execution, will invoke command fail when trying to execute it? Or will invoke command somehow use my admin credentials to bypass the security?

You want the remote computer to display a message to and request input from whatever user is currently logged in on the console?

I believe you will need to somehow launch a new process for the user under their login session for that to work. Otherwise you'll probably run into session 0 isolation.
Assuming the account you use for remoting to the computer has local administrator, I think you can create a scheduled task that runs one time, as the currently logged in user, and then point it to some script file you've either copied to the machine, or on the network.

Judge Schnoopy
Nov 2, 2005

dont even TRY it, pal
Ok so the base idea here is I'm trying to troll a coworker at my company. He said he uses sleep mode every night, I told him it's a terrible idea and to stop doing it, he said he'll continue doing it every day out of spite. I have a powershell script that looks through his event logs for Event 42, sleep mode. If found, it notifies me through write-host. I want to make a very official looking error box saying something to the tune of him being detected for out-of-policy IT practices and to submit his apology in a text box. The apology is then written to a csv with the computer name and date.

Locally, this works great. Remotely, I can get it to search through his event viewer and trigger on the event, but the error box will not appear on his screen (it's either my screen or an error). I've tried using InputBox in VBScript, sending the vbs to the remote computer and using powershell to execute it, but again the box doesn't actually appear since it's not in the user's context.

I'm out of ideas. Using c:\windows\system32\msg.exe works to execute a message on his screen but can't accept input and has my name attached to the window.

The premise sounds extremely trivial and dumb when I'm typing this out but at this point I'm finding it's a good exercise in thinking deeper with powershell to solve problems. Any ideas?

nielsm
Jun 1, 2009



The actual best solution I'd say, would be to use the Task Scheduler to define a task running interactive as the user and have it trigger on the appropriate event. That or PSEXEC are your primary choices for getting a program to run on an interactive desktop from remote.
Or if you do prefer to search through the event log with PowerShell (remotely?) and then trigger it to run from that, use New-ScheduledTask to create one that runs as his user, when he is logged on, right away or a few minutes later.

Judge Schnoopy
Nov 2, 2005

dont even TRY it, pal
I ended up using his local task scheduler and storing the pop up script on his machine. I'll have to check apology.csv manually since he doesn't have write access to my machine.

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

Judge Schnoopy posted:

I ended up using his local task scheduler and storing the pop up script on his machine. I'll have to check apology.csv manually since he doesn't have write access to my machine.

Create a local share and give him write access to a single folder.

PBS
Sep 21, 2015
edit: late to the party

nielsm
Jun 1, 2009



Does anyone know of a convenient way to interactively pick an OU (to create some object in) from AD, in a script? I can probably write something myself to make a basic menu kind of thing, but maybe something already exists.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

nielsm posted:

Does anyone know of a convenient way to interactively pick an OU (to create some object in) from AD, in a script? I can probably write something myself to make a basic menu kind of thing, but maybe something already exists.

If you are just looking for the UI part and not the interacting with AD part:

https://technet.microsoft.com/en-us/library/ff730949.aspx

nielsm
Jun 1, 2009



Ended up writing a simple CLI menu function:
code:
function InteractivePickOU($ouBase, $favorites) {
    $currentList = $favorites | Get-ADOrganizationalUnit -Properties Name,CanonicalName,DistinguishedName
    $selected = $null

    while ($selected -eq $null) {
        Write-Host "Select an OU to place object in:"
        $i = 1
        $currentList | ForEach-Object {
            Write-Host "  $i. $($_.CanonicalName)"
            $i = $i + 1
        }
        $choice = Read-Host "Select an entry from the list, or type a search term"
        $choice = $choice.Trim();

        if ($choice -match "^\d+$") {
            $choice = [int] $choice
            if ($choice -ge 1 -and $choice -le $currentList.Length) {
                $selected = $currentList[$choice-1]
            } else {
                Write-Warning "Invalid choice"
            }
        } elseif ($choice -eq "") {
            Write-Warning "Invalid choice"
        } else {
            Write-Host
            Write-Host "Searching for OUs..."
            $searchTerm = ("*"+$choice+"*")
            $newList = Get-ADOrganizationalUnit -SearchBase $ouBase -Filter { Name -like $searchTerm } -Properties Name,CanonicalName,DistinguishedName
            $resultSize = $newList | Measure-Object | % Count

            if ($resultSize -eq 0) {
                Write-Warning "No results found for search"
            } elseif ($resultSize -ge 15) {
                Write-Warning "Found more than 15 results, truncating"
                $currentList = $newList | select -First 15
            } else {
                Write-Host "Found $resultSize results"
                $currentList = $newList
            }
        }

        Write-Host
    }

    return $selected
}

Roargasm
Oct 21, 2010

Hate to sound sleazy
But tease me
I don't want it if it's that easy
Edit: Solved the below. I was looking for Convertfrom-CSV, e.g. "$CSV = $testData | Convertfrom-CSV"


I'm writing a connection test script that's going to be run from lots of remote client computers which we have little/no control over. I have a CSV of service names, IPs, and ports but would rather keep the data in the script instead of copying down & importing a CSV file on run time. Also do not want to save any new data on the remote computers

Can I keep the CSV data in the script file without writing a PS object/array for it? (i.e. copy the text directly from the .csv into the script)

example:

$testData = "Function,HostName,PublicIP,Port
Circus, gotothecircus.com,69.69.42.42,443
Slides,fun.slidesrule.net, 69.69.42.69,21"

"$csvObject = $testData | import-csv" doesn't work :(

Roargasm fucked around with this message at 18:38 on May 12, 2016

GPF
Jul 20, 2000

Kidney Buddies
Oven Wrangler

Roargasm posted:

Edit: Solved the below. I was looking for Convertfrom-CSV, e.g. "$CSV = $testData | Convertfrom-CSV"

You beat me to it, but I did it a little differently. I put the $testdata into a here-string.
code:
$testdata = @"
Function,HostName,PublicIP,Port
Circus, gotothecircus.com,69.69.42.42,443
Slides,fun.slidesrule.net, 69.69.42.69,21
"@

$csvObject = ConvertFrom-Csv -InputObject $testdata

slightpirate
Dec 26, 2006
i am the dance commander
So good news is I've gotten my script to pull the testing OU into a CSV file, and I can edit it and push it back out to AD.

The bad news is, no matter what I change my $searchbase to, it only pulls from the Testing OU. Any ideas? I've bounced this off of different domain controllers, and ran the script off of a VM that's parked in our DC thinking maybe there was some VPN lag issues with running it from my laptop. I'm sort of at a loss here.

ex.
code:
Testing OU
$SearchBase = "OU=Testing,OU=ACCOUNTS,,DC=domainname,DC=com"

Production OU
$SearchBase = "OU=Facility,OU=Region,OU=Users,OU=ACCOUNTS,DC=domainname,DC=com"

nielsm
Jun 1, 2009



slightpirate posted:

So good news is I've gotten my script to pull the testing OU into a CSV file, and I can edit it and push it back out to AD.

The bad news is, no matter what I change my $searchbase to, it only pulls from the Testing OU. Any ideas? I've bounced this off of different domain controllers, and ran the script off of a VM that's parked in our DC thinking maybe there was some VPN lag issues with running it from my laptop. I'm sort of at a loss here.

That code is just assigning some variables. Are you actually passing that to your Get-ADUser command as well?

slightpirate
Dec 26, 2006
i am the dance commander
Nielsm - Thank you! Admittedly, I was trying to put this together at 4am and totally missed that I had set up my variables for the search, but didn't actually have any instruction in my Get-aduser string to use said variable. It's working great now! I can change the Searchbase variable and it publishes it to a .csv file promptly.

code:
$AllADUsers = Get-ADUser -server $ADServer `
-searchbase $SearchBase `
-Filter * -Properties *

Methanar
Sep 26, 2013

by the sex ghost
Is there a preferred way of enabling remote powershell administration for a bunch of windows 7 computers?

I was thinking about setting a login script with

enable-pssremoting -force -confirm

Judge Schnoopy
Nov 2, 2005

dont even TRY it, pal
Group policy for the powershell remote service works. The single setting sets the service to automatic on startup, opens the listening port, and assigns an acl for allowed IPs initiating the connection. I set it up and was running pssessions without any issues the next day.

slightpirate
Dec 26, 2006
i am the dance commander
So my next little pet project is figuring out how to load a few things and I need some help squaring up the logic on this. The idea here is to take a list of users from a .csv file, see if they are in AD and make some changes to them. If they are present check to see if they are a member of a certain group. If they are, remove them from the group, disable the account, and move the user object to the disabled accounts OU. If they are present in AD, but aren't a member of that group, disable the account and move them to the disabled OU. If the user isn't present at all in AD, write-host that they aren't present in AD and keep processing users.

How do I hand off the first check to the rest of the stack? Here's a rough outline
code:
import csv
Check if user exists in AD
If true1
      check to see if user is part of ADGroup
         
         If true2 - they exist and are part of the group
         remove-adgroup -identity user_that_exists_in_AD
         disable -adaccount -identity user_that_exists_in_AD
         get-aduser user_that_exists_in_AD | move-adobject -targetpath "ou=farts,ou=butts,dc=colon,dc=com"
        
         If False2 - they exist in AD, but aren't a part of the ADGroup
         disable -adaccount -identity user_that_exists_in_AD
         get-aduser user_that_exists_in_AD | move-adobject -targetpath "ou=farts,ou=butts,dc=colon,dc=com"

If false1 (they dont exist at all)
 keep going through the list!

Doug
Feb 27, 2006

This station is
non-operational.

slightpirate posted:

So my next little pet project is figuring out how to load a few things and I need some help squaring up the logic on this. The idea here is to take a list of users from a .csv file, see if they are in AD and make some changes to them. If they are present check to see if they are a member of a certain group. If they are, remove them from the group, disable the account, and move the user object to the disabled accounts OU. If they are present in AD, but aren't a member of that group, disable the account and move them to the disabled OU. If the user isn't present at all in AD, write-host that they aren't present in AD and keep processing users.

How do I hand off the first check to the rest of the stack? Here's a rough outline
code:
import csv
Check if user exists in AD
If true1
      check to see if user is part of ADGroup
         
         If true2 - they exist and are part of the group
         remove-adgroup -identity user_that_exists_in_AD
         disable -adaccount -identity user_that_exists_in_AD
         get-aduser user_that_exists_in_AD | move-adobject -targetpath "ou=farts,ou=butts,dc=colon,dc=com"
        
         If False2 - they exist in AD, but aren't a part of the ADGroup
         disable -adaccount -identity user_that_exists_in_AD
         get-aduser user_that_exists_in_AD | move-adobject -targetpath "ou=farts,ou=butts,dc=colon,dc=com"

If false1 (they dont exist at all)
 keep going through the list!

(disclaimer: I'm no pro)

code:
users = import csv
foreach user in users
     if get-aduser
          if get-adgroup member
               do your stuff
          else
               do other stuff
    else
              write-host who?
This should work, no?

Mo_Steel
Mar 7, 2008

Let's Clock Into The Sunset Together

Fun Shoe
e: ^^ That'd work too.

slightpirate posted:

So my next little pet project is figuring out how to load a few things and I need some help squaring up the logic on this. The idea here is to take a list of users from a .csv file, see if they are in AD and make some changes to them. If they are present check to see if they are a member of a certain group. If they are, remove them from the group, disable the account, and move the user object to the disabled accounts OU. If they are present in AD, but aren't a member of that group, disable the account and move them to the disabled OU. If the user isn't present at all in AD, write-host that they aren't present in AD and keep processing users.

How do I hand off the first check to the rest of the stack? Here's a rough outline
code:
import csv
Check if user exists in AD
If true1
      check to see if user is part of ADGroup
         
         If true2 - they exist and are part of the group
         remove-adgroup -identity user_that_exists_in_AD
         disable -adaccount -identity user_that_exists_in_AD
         get-aduser user_that_exists_in_AD | move-adobject -targetpath "ou=farts,ou=butts,dc=colon,dc=com"
        
         If False2 - they exist in AD, but aren't a part of the ADGroup
         disable -adaccount -identity user_that_exists_in_AD
         get-aduser user_that_exists_in_AD | move-adobject -targetpath "ou=farts,ou=butts,dc=colon,dc=com"

If false1 (they dont exist at all)
 keep going through the list!

Assuming you're working with a csv file similar to the import-csv help dialog, like so:

code:
Name,Department,Title
Pilar Ackerman,Research,Manager
Jonathan Haas,Finance,Finance Specialist
Ken Myer,Finance,Accountant
I'd probably just do foreach-object. Something like this:

code:
$users = Import-Csv users.csv

$users | ForEach-Object {
    $user = $_.name
    if(Get-ADUser -filter {name -eq $user}) {
        "$user found"

    } else { "Couldn't find $user in the ADUsers" }
}
eta: Is there a reason to check if they're in the group other than keeping the output clean? Based on your rough outline it looks like you're doing the same thing to the user regardless, and only checking the group to make sure to remove them from it, but I would expect remove-adgroupmember to just throw a message out if the user wasn't already in the group and keep going with the rest.

Mo_Steel fucked around with this message at 18:33 on May 26, 2016

sloshmonger
Mar 21, 2013
To check for group membership, you can use something along these lines. It checks if the group is a part of the ADuser.memberof property, rather than looking if the user is a part of the group. Roughly equivalent, but you'll be loading up the user object anyway and it'll usually be slightly quicker to iterate through groups rather than members, though it's fractions of a millisecond.

code:
$ADuser = get-aduser -properties * -filter {name -eq $user}
$ADgroup = get-adgroup "Group Name"
if($ADuser.memberof.Value -eq $ADgroup.distinguishedName){
	Remove-AdgroupMember $AdGroup -members $AdUser -server $WritableADServer -Credential $ADCredential 
	Write-Host "Removed $($ADUser.SamAccountName) from $($ADGroup.DistinguishedName)"
}

nielsm
Jun 1, 2009



If you want to remove users from particular groups when disabling them, I'd do something like what sloshmonger suggests, but maybe turn it around a bit.

code:
$badgroups = @('CN=badgroup1,OU=groups,...', 'CN=badgroup2,OU=groups,...')
$users = import-csv users.csv
$users | foreach-object {
  $user = get-aduser $_.username -properties memberof
  $user.memberof | foreach-object {
    if ($badgroups -contains $_) {
      remove-adgroupmember -identity $_ -members $user.samaccountname
    }
  }
}
If you actually just want to remove every group from the user, just do the Remove-ADGroupMember unconditionally. Or you could have a list of "good" groups instead of "bad" ones.

There's really lots of ways to handle it.

Swink
Apr 18, 2006
Left Side <--- Many Whelps
I've just upgraded my Win7 laptop to Win10. I'm missing the package management cmdlets. Find-module, find-package etc. What gives?


There they are, I had to run as admin. Go me!

Swink fucked around with this message at 12:21 on May 27, 2016

slightpirate
Dec 26, 2006
i am the dance commander

Mo_Steel posted:

e: ^^ That'd work too.


Assuming you're working with a csv file similar to the import-csv help dialog, like so:

code:
Name,Department,Title
Pilar Ackerman,Research,Manager
Jonathan Haas,Finance,Finance Specialist
Ken Myer,Finance,Accountant
I'd probably just do foreach-object. Something like this:

code:
$users = Import-Csv users.csv

$users | ForEach-Object {
    $user = $_.name
    if(Get-ADUser -filter {name -eq $user}) {
        "$user found"

    } else { "Couldn't find $user in the ADUsers" }
}
eta: Is there a reason to check if they're in the group other than keeping the output clean? Based on your rough outline it looks like you're doing the same thing to the user regardless, and only checking the group to make sure to remove them from it, but I would expect remove-adgroupmember to just throw a message out if the user wasn't already in the group and keep going with the rest.

I'm not well versed in how this output can be carried into other commands. So lets say John.Smith is an active user account and is part of the ProjectUsers AD group, and is the Users OU. Jane.Williams is an active user account who is not in the ProjectUsers AD group, but is in the Users OU. The goal is to get both of them into the Disabled Users OU, without group memberships (aside from Domain Users) and to have their accounts disabled.

I know that's a little redundant, but its cleaner on our side. Our auditors are weird. Normally I'd do this on a onesy-twosy basis, but our Auditor sent us a list of about 360 users that need to be stripped, disabled, and shoved into a different OU. This is all really good information guys, and I really appreciate the help.

Swink
Apr 18, 2006
Left Side <--- Many Whelps
Today I wrote a script to poll a bunch of IP addresses (I was trying to find a specific host among an otherwise empty subnet. Document your networks plz)

It was super quick and dirty and I used this little trick which I thought was worth sharing

"1..100" builds a nice array of numbers.

code:

1..254 | foreach ($element) {

    if (Test-Connection 192.168.1.$_ -Quiet) {

        Write-Host "Found something on: .$_"
        
   }

   }

slightpirate
Dec 26, 2006
i am the dance commander
Here's what I have so far, and it seems to work pretty well. The only complaints I have is that when it can't find a username in AD, it outputs a big block of text. I'd prefer it to just make a single line of "$user.samaccountname was not found!" The second issue is that it asks for a confirmation for every remove-adgroupmember, any ideas on how to set a blanket confirmation? Any good way to set a log file output of what its doing?

Other than that, it seems to pass along everything else fine. Users that are part of of the group or not, so long as they are on the list get stripped of the specified AD group, disabled, and filed away in a disabled users OU!

code:
Import-Module activedirectory
#variable set
$Removedgroup = @('CN=Powershell Group,OU=Testing,OU=Accounts,DC=domainname,DC=com')
#find users, move them to the disabled OU and disable the account. 
$users = Import-Csv -path "C:\powershell\test\test.csv" | Foreach {Get-ADUser $_.SamAccountName | Move-ADObject -TargetPath "OU=Testing,OU=Accounts,DC=domain,DC=com" -PassThru | Disable-ADAccount -passthru |
}
#Removes users from Selected AD group.Use Remove-ADGroupMember unconditionally if all groups need to be removed
$users | foreach-object {
 $user = get-aduser $_.SamAccountName -properties memberof
 $user.memberof | foreach-object {
    if ($Removedgroup -contains $_) {
      remove-adgroupmember -identity $_ -members $user.samaccountname}
 }
 }

Mo_Steel
Mar 7, 2008

Let's Clock Into The Sunset Together

Fun Shoe

slightpirate posted:

Here's what I have so far, and it seems to work pretty well. The only complaints I have is that when it can't find a username in AD, it outputs a big block of text. I'd prefer it to just make a single line of "$user.samaccountname was not found!"

If it's getting angry red text when you call an invalid user you can try something like this when calling Get-ADUser (I haven't tried it in a for loop, check to make sure one errorvariable doesn't carry over or something silly to the next name and fill your screen with messages):

code:
$user = get-aduser $_.SamAccountName -properties memberof -ErrorAction SilentlyContinue -ErrorVariable UserError
if ($UserError) {
 "$user.samaccountname was not found!" 
}

slightpirate posted:

The second issue is that it asks for a confirmation for every remove-adgroupmember, any ideas on how to set a blanket confirmation?

By default it asks for confirmation, but if you're really sure, use the -Confirm switch:

code:
remove-adgroupmember -identity $_ -members $user.samaccountname -Confirm $False


slightpirate posted:

Any good way to set a log file output of what its doing?

Once you have it in a saved script, you could call it like so:

code:
.\myScript.ps1 | Out-File "C:\Users\Bob\Desktop\log.txt" -append -noclobber
That should push any outputs that would appear in the PS window to a text file; some commands will let you run them verbose so you can log that way, or you can just drop in text in your script in quotes like I posted above with "$user found" and it'll dump it out into the log.

Mo_Steel fucked around with this message at 06:41 on Jun 3, 2016

nielsm
Jun 1, 2009



Get-ADUser is actually rather annoying re. error handling. When you use the form with -Identity (the default) it will throw an exception and not raise an error, when no object is found. Passing -ErrorAction does nothing about that.

You have two options:
1. Catch the exception with try..catch
2. Search with -Filter instead
code:
try {
  $u = Get-ADUser doesnotexist
}
catch {
  Write-Host "not found"
}
code:
$u = Get-ADUser -Filter { samaccountname -eq "doesnotexist" }
if (-not $u) { Write-host "not found" }

lol internet.
Sep 4, 2007
the internet makes you stupid
So... a job I am applying for asked me to do create a script as part of the interview process. Kinda strange because what I'm doing doesn't require too much scripting but anyways I got most of it done but I'm just hoping someone can point me in the right direction for a couple things last things. Not looking for an exact answer as I am going to get questioned on the script code I write anyways but perhaps which cmdlets help files I should be looking at or pointing me in the right direction.

- Script invokes user enter 1 to 10 names separated by commas
- I put assign those names into a variable array using .split(".")

How would I check the array to make sure there is between 1-10 entries and not blank? I assume use an if statement with <=1 -And >=10 but I'm not sure how to write the syntax for it to check the actual variable?

Another thing is to test a folder path where prior to running export-csv to it, the path will be inputted by a user

and.. lastly is there anyway to export to csv in a nice table like what appears in format-table since that doesn't work with export-csv ?

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
CSV is always going to look the same, it's a standard.

Format-Table always needs to be near the end of your manipulation because it adds a bunch of junk to make things pretty. Leave everything as objects as long as possible.

Adbot
ADBOT LOVES YOU

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
Another thing, to help you help yourself.

Create a string

$UserInput = "a,b,c,d"

Now type this:

$UserInput | Get-Member

It will give you a list of "Methods"

One of those methods will turn your string into something very useful.

For syntax, you'll type something like:
$UserInput.ToUpper()

The parentheses sometimes need stuff inside them, you can tell because the Definition for the method will have the word Params inside.
So if you use the TrimEnd method, you need to specify how many characters to trim.

Edit:

For eliminating empty elements, I don't want to give away too much, you should be able to Google this, but you might find a very good answer that looks almost as impenetrable as that goofy linux fork bomb :(){:|:&};:

There are a few aliases and special characters in Powershell you should know.

? = Where-Object
% = ForEach-Object
| = Pipe, commandlets are like little factories and the pipe makes the object from the last command shoot into the next one.
$_ = The input I got from the pipe

Dr. Arbitrary fucked around with this message at 01:15 on Jun 7, 2016

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply