Get AD Group Membership recursivly using PowerShell - windows

What is the cleanest (and fastest) way to get ALL groups that a single user is a member of.
Im using PowerShell 2.0 to count the logged in users in Citrix and devide them into groups from the Active Directory. All users are member of 1 of the subgroups of a group called "VDI-Billing", but the number of nested groups between the user and the VDI-Billing group is not always the same. So i want to be able to get all groups (including nested ones) to compare to the list of members from the VDI-Billing group (1st level) so i get an overview.
Example:
VDI-Billing has a member group NL-VDI-T-Systems. That has multiple groups (that by themselves have multiple groups). But the overview must count all users (sub)member of NL-VDI-T-Systems.
So in the overview i should get:
NL-VDI-T-Systems: 22
ITA-VDI-T-Systems: 25
And so forth.
Anyone know a neat little trick?

We write scripts that do this at my work all the time! With the Quest ActiveRoles Management Tools, a free snapin that makes working with Active Directory objects in Powershell WAY easier.
Install the free Quest ActiveRoles Management Tools from Quest
Add the PSSnapin to your profile so that you can access all the Powershell AD tools from the console - Add-PSSnapin Quest.ActiveRoles.ADManagement. If you want to write scipts that use the AD tools, simply add the command to the first line of your script.
Run the following command to get all direct and nested group that a user is a member of: Get-QADUser 'DOMAIN\USER' | foreach -Process {$_.memberof, $_.nestedmemberof} you can pipe this to a text file or CSV if you want by adding the Out-CSV or Out-File cmdlets at the end of the command.
This works like a charm for me. Let me know if you have any questions!
~Dan

The best way to do it is to take advantage of the TokenGroups attribute, instead of performing the recursive expansion on your own. You can find examples here and here.
EDIT: A more succinct example

Related

Ruby: how to access group info from /var/db/group.db

RHEL/CentOS 8.x, Ruby 2.5.5, irb 0.9.6
We have a specialized set up that keeps user and group information in /usr/local/etc/[user|group] and a custom Makefile to add that information to /var/db/[group|passwd].db accordingly.
I am trying to get the list of groups a user belongs to, in Ruby, after login.
I'm relatively new to the language, and have just read the documentation for the Etc module but that seems to exclusively work with the /etc/ filesystem. Not outrageous tbh.
Is there an easy way to access the Berkley DB info in /var/db or am I going to have to iterate through the /usr/local/etc/group file to get this information?
I suspect that the documentation of that module is heavily simplified, heavily outdated, or both. I am almost 100% sure that Etc will use the OS-provided standard library functions instead of going off and parsing system files by itself. (Why would the Ruby developers write parsers for the configuration files of every single OS they support, instead of just calling a POSIX function?)
You can confirm this suspicion using strace.
If you look at how the Ruby Etc module is structured, it maps 1:1 to the POSIX functions:
Etc::gegtrent: gets the next group entry.
Etc::endgtrent: stops iterating the groups.
Etc::settrent: resets the iteration.
Here are the POSIX functions for comparison:
endgrent, getgrent, setgrent – group database entry functions
In my case, I am testing this on macOS (which already has specialized user handling), but furthermore, I tested it on my company laptop, which is macOS joined to a Microsoft ActiveDirectory Domain. My user account and its group aren't even mentioned in either /etc/passwd or /etc/group, and yet, I can easily read them with Etc. I am very sure that the Ruby developers did not implement ActiveDirector just to accommodate my personal weird use case, so it must use system functions.
So, if you have e.g. a uid and want to know what groups it belongs to, you need to first get its name, and then search the groups. Unfortunately, it looks like the provided iterator wrappers do not support the common idiom that not supplying the block returns an Enumerator, so you have to create one yourself.
require 'etc'
uid = 0 # for example
username = Etc.getpwuid(uid).name
groups = Etc.enum_for(:group).select {|group| group.mem.include?(username) }
p groups

Writing a linux bash script to add a user to a group in eDirectory

I want to write a shell script to bulk add users to a group in eDirectory. The trouble is, I'm not sure where to begin regarding LDAP calls and whatnot for this task. Can anyone point me to a helpful resource or demonstrate a method for accomplishing this task?
Using straight-up LDAP would be fine, but I imagine that there must exist faster utilities/constructs that can be used in shell.
NetIQ's LDAPConfig or NDSConfig utilities seemed promising, but I don't see anything about adding a user to a group in the documentation.
Turns out, I don't have to code a solution to this. Instead of selecting each item and adding them to a group, there's a tool buried deep inside iManager that allows you to select all users matching a set of conditions and add them to a group.

How can I resolve MSI paths in VBScript?

I am looking for resolving the paths of files in an MSI with or without installing (whichever is faster) from outside an MSI, using VBScript.
I found a similar query using C# instead and Christpher had provided a solution, below: How can I resolve MSI paths in C#?
I am going through the very same pain now but is there anyway to achieve this using WindowsInstaller object in VBScript, rather than go with endless queries through SQL Tables of MSI back and forth to achieve the same. Though any direction would be welcoming as I have tried tested whatever I can with very limited success.
yes there is a solution without installing the msi and using vbscript.
there is a very good example in the Windows Installer SDK called "WiFilVer.vbs"
using that example i've thrown together an quick example script that does exactly what you need.
set installer = CreateObject("WindowsInstaller.Installer")
const READONLY = 0
set db = installer.OpenDataBase("<FULL PATH TO YOUR MSI>", READONLY)
set session = installer.OpenPackage(db, READONLY)
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")
set view = db.OpenView("SELECT File, Directory_, FileName, Component_, Component FROM File,Component WHERE Component=Component_ ORDER BY Directory_")
view.Execute
set record = view.Fetch
do until record is nothing
file = record.StringData(1)
directoryName = record.StringData(2)
fileName = record.StringData(3)
if instr(fileName, "|") then fileName = split(fileName, "|")(1)
wsh.echo(session.TargetPath(directoryName) & fileName)
set record = view.Fetch
loop
just add the path to your MSI file.
tell me if you need a more detailed answer. i will have some more time to answer this in detail this evening.
EDIT the promised background (and why i need to call ConstFinalize)
naveen actually MSDN was the only resource that can give an definitive answer on this, but you need to know where and how to look since windows installer ist IMHO a pretty complex topic.
I really recommend a mix of the msdn installer function reference, the database reference, and the examples from the windows installer SDK (sorry couldn't find a download link, i think its somewhere hidden in the like 3GB windows SDK)
first you need general knowledge of MSIs:
an MSI is actually a relational database.
Everything is stored in tables that relate to each other.
(actually not everything, but i will try to keep it simple ;))
This database is interpreted by the Windows Installer,
this creates a 'Session'
also some parts are dynamically resolved, depending on the system you install the msi on,
like 'special' folders similar to environment variables.
E.g. msi has a "ProgramFilesFolder", where windows generally has %ProgramFiles%.
All dynamic stuff only exists in the Installer session, not the database itself.
In your case there are 3 tables you need to look at, take care of the relations and resolve them.
the 'File' table contains all Files, the 'Component' table tells you which file goes into which directory and the 'Directory' table contains all information about the filesystem structure.
Using a SQL Query i could link the Component and File table to find the directory name (or primary key in database jargon).
But the directory table has relations in itself, its structured like a tree.
take a look at this example directory table (taken from the instEd MSI)
The columns are Directory, Directory_Parent and DefaultDir
InstEdAllUseAppDat InstEdAppData InstEd
INSTALLDIR InstEdPF InstEd
CUBDIR INSTALLDIR hkyb3vcm|Validation
InstEdAppData CommonAppDataFolder instedit.com
CommonAppDataFolder TARGETDIR .
TARGETDIR SourceDir
InstEdPF ProgramFilesFolder instedit.com
ProgramFilesFolder TARGETDIR .
ProgramMenuFolder TARGETDIR .
SendToFolder TARGETDIR .
WindowsFolder_x86_VC.1DEE2A86_2F57_3629_8107_A71DBB4DBED2 TARGETDIR Win
SystemFolder_x86_VC.1DEE2A86_2F57_3629_8107_A71DBB4DBED2 WindowsFolder_x86_VC.1DEE2A86_2F57_3629_8107_A71DBB4DBED2 System
The directory_parent links it to a directory. the DefaultDir contains the actual name.
You could now resolve the tree by yourself and replace all special folders(which in a vbscript would be very tedious)...
...or let the windows installer handle that (just like when installing a msi).
now i have to introduce a new thing: Actions (and Sequences):
when running (installing, removing, repairing) an msi a defined list of actions is performed.
some actions just collect information, some change the actual database.
there are list of actions (called sequences) for various things a msi can do,
like one sequence for installing (called InstallExecuteSequence), one for collecting information from the user (the UI of the MSI: InstallUISequence) or one for adminpoint installations(AdminExecuteSequence).
in our case we don't want to run a whole sequence (which could alter the system or just take to long),
luckily the windows installer lets us run single actions without running a whole sequence.
reading the reference of the directory table on MSDN (the remarks section) you can see which action you need:
Directory resolution is performed during the CostFinalize action
so putting all this together the script is easier to read
* open the msi file
* 'parse' it (providing the session)
* query component and file table
* run the CostFinalize action to resolve directory table (without running the whole MSI)
* get the resolved path with the targetPath function
btw i found the targetPath function by browsing the Installer Reference on MSDN
also i just noticed that CostInitialize is not required. its only required if you want to get the sourcePath of a file.
I hope this makes everything clearer, its very hard to explain since it took me like half a year to understand it myself ;)
And regarding PhilmEs answer:
Yes there are more influences to the resolution of the directory table, like custom actions.
keeping that in mind also the administrative installation might result in different directorys (eg. because different sequence might hold different custom actions).
Components have conditions so maybe a file is not installed at all.
Im pretty sure InstEd doesnt take custom actions into account either.
So yes, there is no 100% solution. Maybe a mix of everything is necessary.
The script given by weberik (deriven from MS SDK VB code) is very good, because it makes it easy to analyse the directory table without an own algorithm (which is a mid-size effort to do it in a loop or with a recursion algorithm).
But it gives not a 100% perfect view for all files, see below.
The method of the script is semi-dynamic (can be extended by other actions), but in effect it gives only the static directory structure, similar to a default administrative install or advanced MSI viewers.
Normally this is enough and what we want.
But be aware, that this is not the 100% solution (Knowing before exact the path of each file afterwards). That does mean, this will not give you always the correct paths in some cases:
You use command line parameters which substitute predefined directory table entries.
The MSI uses custom actions which change paths.
Especially it is not guaranteed, that every file is installed. There may be optional conditions and features and this may depend on the install environment.
In effect, a 100% solution is very very hard to achieve, without installing really. It comes near to reprogram nearly the whole windows installer engine. So simplifications are normally sufficient and accepted.
But you can extend the method to cover custom actions, e.g. with adding a line "session.DoAction(..)" for each additional action needed. Or to include command line parameters.
Sometimes it can be tricky. The easier the structure of the MSI is, the more likely it is that you succeed without more efforts.
Alternative to write an own program:
The question is, what you really want to find out, and if it is really necessary to program it:
If you don't want to write an automatical every-day MSI analyzer maybe the following is sufficient for you:
First tip: install the MSI with "msiexec /a mysetup.msi TARGETDIR="c:\mytestpath" . (similar restrictions as script above by weberik)
If the MSI has not used custom actions to change pathes including forgetting to add to the admin sequence ("forgetting" should be taken as the normal case for 99% or existing setups :-), you get the filestructure like if you install "really" with some special namings for the Windows predefined folders which you will find out easily.
If the administrative install lacks some folders, it is often a better idea of fixing the custom action (adding to the admin sequence) and using this scenario as your primary test case.
The advantage is, that only you limit the dynamics used by admin install. BTW, you can use the same command line params or path settings custom actions as in real install.
Second tip: Google for the InstEd tool , go to the file or component table and you will see the resulting MSI paths in the same static way as with the mentioned VB-script after calling CostInitialize/CostFinalize. For human view such an editor view maybe better.
For automatic testing and improvements or accuracy, you need an own program of course.
For those of you that mentioned snippet given is a good starting point. :-)
The rest of you should live easier with one of the two given methods without programming.

Changing permissions and users that can view a directory in windows

Here is my dilemma. We have a server with a path, say \server1\data$\foo\bar\stuff. I'm using Windows 2005 Server for this. Now, for \stuff, I'd like to add something like 150 users that I have in a CSV file (can get it from Excel).
I can parse the CSV file rather easily (most languages have libraries for this), but I don't know anything about Windows permissions (used Linux for quite a bit of my career prior).
Can I do this in PHP?
You can do this from the command line via active directory commands through DOS or Powershell. I list some references and examples from references below.
I would suggest adding these users to a group. This example adds a user to a group:
dsmod group "CN=csvpeeps,OU=Distribution Lists,DC=Contoso,DC=Com" -addmbr "CN=jimmyCrackedCorn,CN=Users,DC=Contoso,DC=Com"
Reference: http://technet.microsoft.com/en-us/library/cc732423(WS.10).aspx
To create a group, see the dsadd command. Example of creating a group:
dsadd group cn=csvpeeps,cn=users,dc=northwindtraders,dc=com
One option on Windows, through DOS is Cacls, iCacls, and other variants. This example adds Read permissions to a resource for the group, 'Power Users':
CACLS \server1\data$\foo\bar\stuff. /E /G "csvpeeps":R
Reference: http://ss64.com/nt/cacls.html
I have read that CACLS can be buggy, so you may want to look into another variant of CACLS. I highly recommend http://ss64.com
If you don't know the exact OU/CN, etc for users or groups, you can check it out with something like this:
dsquery user -name jimmyCrackedCorn
Similarly for groups
dsquery group -name csvpeeps
Let me know if you get stuck and I can put more effort into this. Powershell is another option worth looking into.
Ok, here's my approach. After looking at the issue. I think PowerShell is the way to go. I have a CSV file as mentioned above which is delimited with commas and have a the following format:
Last Name, First Name
Smith, Bob
Corn Amy
Etc.
What I'd like to do is find out the AD usernames of these people (Amy Corn will be acorn according to our internal format) and then insert them into a specific directory so that read-only directory has those users that can view its contents. Any help is appreciated.

General Purpose Filter As You Type (aka typeahead, Incremental find, autocomplete) is it out there?

Background
Lately I've become a fanatic that everything I type while working on a computer should be compatible with "DRY". If there's anything I have to type more than once in any context, I want some kind of user-aware auto-complete option to do some of the work for me -- always -- no exceptions.
Having to work under Windows, I've looked at GUI solutions to make this insane goal a reality.
The (almost) optimal solution
If you have a moment, open up Firefox 3.0 and type a few keystrokes into the address bar. You will notice that it performs a kind of Incremental Autocomplete based on space-separated sub-strings of whatever you type. Another place in Firefox that does something similar is the about:config URL.
This is sub-optimal, because I don't want this in Firefox only. I want to use this everywhere.
The Question
Does anyone out there know of a widget or app that does nothing but insanely good incremental auto-complete that can be used as a general purpose "run everywhere" tool? Something that allows the user to: 1) maintain one or more "completion candidate files"; 2) pick one of those files as the source for Firefox 3.0 style completion; 3) return the result (or blank if the user canceled), and do those three things only?
Details
Here's how it should work:
STEP1: user saves or more csv file(s) (or other easy-edit format) somewhere in his hard-drive
STEP2: user creates a Windows Script Host script or a batch file (or whatever) instantiates the FilterAsYouType GUI
STEP3: user runs the script file, and the script file instantiates the GUI, telling it which CSV file to use as the source of all potential completions
STEP4: the user either chooses one of the completions, supplies his own text that is not in the list, or cancels out without supplying anything
STEP5: when the user is done the script saves the result to a variable and does something with it
Here is some pseudo-code for the script:
include "GenericTypeaheadWidget";
var gengui = new GenericTypaheadWidget('c:\docs\favorite_foods.csv');
var fave_food = gengui.get_user_input();
if(fave_food != ''){
alert('you chose '+fave_food+'!');
}
The rationale
The goal is to just have a way to always be able to do auto-completions from a list of arbitrary items, even if the list is a couple thousand items, and not have to rely on it being built into some IDE or standalone application that only accepts certain kinds of input or has an overly-complicated API relative to the simplicity of this task.
CSV (or text or sqlite database) would provide a way for me to self-generate "candidate lists" or "history logs" and then just use those logs as the source of the possible completions.
The disclaimer
I've tried several GUI "launcher" programs, command-line engines like power-shell and scripting shells, the regular plain old command-line history with varying degrees of satisfaction. The problem with these is they all do extra superfluous stuff like searching directories or built-in commands. I just want nothing but whatever is in the CSV file I happen to be pointing at.
I'm wondering if there is any simple tool that does nothing but what I'm describing above.
UPDATE: It looks like this question is very closely related to Graphical Command Shell, which captures the essential idea presented here.
You should really try Launchy - it's exactly what you're looking for, a "run anything" with intelligent autocompletion. It completely changes the way you interact with a Windows PC.
And it has open source-code, so you can borrow its autocompletion code if you want to roll your own interface.

Resources