Powershell, retrieve users manager - powershell

I'm creating a csv type org chart and was just wondering what would be the preferred to retrieve a users manager, manager's manager, ... etc up to the highest position. Currently i'm using:
[string]$man = $userEntry.manager
[array]$manName = $man.split('=,')
$manager = $manName[1]
$item.Cells.Item($i,1) = $userEntry.name.value
$item.Cells.Item($i,2) = $userEntry.description.value
$item.Cells.Item($i,3) = $manager.ToString()
then running get-QADobject to find the next manager by their DN.
but there must be a much cleaner way!
Thanks

If I'm understanding you correctly, you want to follow the chain of command all the way up to the very top (where presumably that person has no manager?). In that case, you need to recursively walk up the tree.
Untested pseudocode as I don't have a domain handy at the moment to test with:
Function Get-Manager {
params(
[string]$username
)
$userEntry = get-qaduser $username
[string]$man = $userEntry.manager
if (-not ($man -eq $null)) {
[array]$manName = $man.split('=,')
$manager = $manName[1]
"$manager is the manager of $username";
Get-Manager $manager
}
}
This will come to a halt once a user has no manager. In my organization's case, our CEO is listed as his own manager, so I'd change the above code to look for the manager to be non-null or equal to the user, so that either of those conditions being true broke the loop.

Related

Add RoleAssignment Member to SPItem Using PowerShell

I will be executing a script to remove permissions from a SPitem. However, a rollback plan is required and I am required to create a separate script which will add the permission of the user back to the SPitem if required.
Below is my code snippet which removes a user from the SPitem:
ForEach ($RDfolderId in $RDfolderSplit)
{
$query = New-Object Microsoft.SharePoint.SPQuery
$query.ViewXml = "#<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='Title' /><Value Type='Text'>$RDfolderId</Value></Eq></Where></Query></View>"
$RDfolder = $RDlist.GetItems($query)
foreach($role in $RDfolder.RoleAssignments)
{
if ($role.Member.Name.Equals($userToAction))
{
#$RDitem.BreakRoleInheritance($true)
#$RDitem.RoleAssignments.RemoveById($roleAssignment.Member.ID)
#$RDitem.Update()
}
}
}
I have seen code samples online on adding roles back to the SPitem. However, there is an additional field RoleDefinitions declared.
Is it compulsary to have the value declared when adding a user to a SPitem?
Below is the code sample for adding:
$web = Get-SPWeb http://sp-2010
$account = $web.EnsureUser("SHAREPOINT\mray")
$role = $web.RoleDefinitions["Contribute"] #is this value compulsory?
$list = $web.Lists["Shared Documents"]
$list.BreakRoleInheritance($true)
$assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($account)
$assignment.RoleDefinitionBindings.Add($role)
$list.RoleAssignments.Add($assignment)
$list.Update()
$web.Dispose()
source
Short answer - Yes.
Let's break this sample up and explain each part:
$web = Get-SPWeb http://sp-2010
$web - SharePoint Web object aka. Site we are working on.
$account = $web.EnsureUser("SHAREPOINT\mray")
$account - User account we are working with.
$role = $web.RoleDefinitions["Contribute"] #is this value compulsory?
$role - This is the Role Definition aka permissions like Contribute/Read/Approve. Yes. This is mandatory as it is the permissions you are going to add back.
$list = $web.Lists["Shared Documents"]
$list - The List we are working with.
$list.BreakRoleInheritance($true)
BreakRoleInheritance - This is if we need unique permissions on the List and to turn inheritance off. We don't have to do this every time, and likely in this example, you don't have to break inheritance.
Now, we are onto the permissions pieces.
$assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($account)
$assignment - First, we need to get all the SharePoint roles currently assigned to our user.
$assignment.RoleDefinitionBindings.Add($role)
Add($role) - Add the Role Definition i.e. "Contribute" to the user object. This does nothing to the list on SharePoint.
$list.RoleAssignments.Add($assignment)
Add($assignment) - Add user with the new permissions to the List object. This does nothing to the list on SharePoint. We are manipulating the end state of the list that we want.
$list.Update()
Update - Now do something on SharePoint. Actually apply the changes we have made to the List object to SharePoint.
$web.Dispose()
Dispose - cleanup our objects.
Now. Saying all of that. This is a good script for setting permissions. You also have a script for removing permissions. The point of a rollback script is that you need to record what those permissions originally were before you remove them. i.e. once you remove them, there isn't a magic undo button. ;-)

Get the "plain" (end-user readable) name of UWP apps installed on a system

I'm using the following PowerShell script to retrieve and save to a text file the list of UWP apps on a system. It gets the ID, name (system name) and packagefamilyname.
In addition to the name, I'm looking for a way to retrieve the plain name of the app: for example, "OneNote" instead of "Microsoft.Office.OneNote". Ideally, this name would also be localized: for example, "Calculatrice" (on a French system) instead of "Microsoft.WindowsCalculator".
I found this list of info retrieved by Get-AppxPackage but nothing like an end-user readable name... I'm not very familiar this area of expertise. Any help would be appreciated.
$installedapps = get-AppxPackage
$ids = $null
foreach ($app in $installedapps)
{
try
{
$ids = (Get-AppxPackageManifest $app -erroraction Stop).package.applications.application.id
}
catch
{
Write-Output "No Id's found for $($app.name)"
}
foreach ($id in $ids)
{
$line = $app.Name + "`t" + $app.packagefamilyname + "!" + $id
echo $line
$line >> 'c:\temp\output.txt'
}
}
write-host "Press any key to continue..."
[void][System.Console]::ReadKey($true)
[Completed updated]
You can do this pretty easily in C#. You have to reference the correct WinMDs from the Windows SDK (the actual directories will change depending on SDK version):
C:\Program Files (x86)\Windows Kits\10\References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd
C:\Program Files (x86)\Windows Kits\10\References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd
If you can't build a stand-alone EXE and just want pure PowerShell, you might be able to reference the WinMDs %systemroot%\system32\winmetadata. The code is pretty simple (I avoided await since I don't know if PowerShell has that):
// using Windows.Management.Deployment;
static void Main(string[] args)
{
GetList();
}
static void GetList()
{
var pm = new PackageManager();
var packages = pm.FindPackagesForUser("");
foreach (var package in packages)
{
var asyncResult = package.GetAppListEntriesAsync();
while (asyncResult.Status != Windows.Foundation.AsyncStatus.Completed)
{
Thread.Sleep(10);
}
foreach (var app in asyncResult.GetResults())
{
Console.WriteLine(app.DisplayInfo.DisplayName);
}
}
}
I spent some time looking for this today, and finally came up with a solution that doesn't involve a page of code, or digging around in files/registry/etc. Put the below two lines in a script or function, and it will return PoSh-friendly output which you can then pipe into ForEach-Object, Where-Object, Sort-Object, Export-CSV, etc.
$PkgMgr = [Windows.Management.Deployment.PackageManager,Windows.Web,ContentType=WindowsRuntime]::new()
$PkgMgr.FindPackages() | Select-Object DisplayName -ExpandProperty Id
The .FindPackages() method also has an overload which takes a Family Name, but the docs lead me to believe it can only accept exact names, not wildcard matches. So unless you know exactly what you are looking for, I am guessing it is best to retrieve the list of all packages, and then do your own searches on that list.
The docs do say that this will return packages for all users, and that it requires admin/elevated rights to run.

Writing a hashtable value to an attribute

Powershell newbie here, my first script.
I have user objects with an AD custom attribute named tvCode with a values of 123456 or 6787682 or 983736 etc.
I would like to script something that will get the tvCode value from the user object
When:
123456 = Sony
6787682 = Samsung
9837343 = LG
Write the value of "Sony" or "Samsung" or "LG" to the "City" attribute of the user object.
Looks like i may need to use a hashtable.
If possible do this for a specific OU
hope this makes sense
thanks
function Convert-TVCode {
Param
(
[parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
[String[]]
$Code
)
Process {
foreach ($C in $Code) {
switch ($C) {
"123456" {$brand = "Sony"}
"6787682" {$brand = "Samsung"}
"9837343" {$brand = "LG"}
default {
$brand = $null
Write-Warning "$C not included in switch statement. Returning"
return
}
}
if ($brand) {
Write-Verbose "Code '$C' matched to Brand '$brand' -- searching for users to update"
Get-ADUser -Filter "tvCode -eq '$C'" | Set-ADUser -Replace #{tvCode=$brand}
}
}
}
}
This function will allow you to update any users that have their tvCode attribute set as one of the target numerical values. You can have it hit multiple codes at once as well.
Examples:
Convert-TVCode -Code 123456
Convert-TVCode -Code 123456,6787682
Convert-TVCode -Code 123456,6787682,9837343 -Verbose
Update the switch statement in the function to customize it to your actual values and let me know if you have any questions!

Comparing AD sites and SCCM boundaries

I m trying to compare the Active directory sites with SCCM boundaries, by using the below powershell scripts, but its not giving the output as expected.
There are 3 AD sites actually available in SCCM, however the script gives me an output that there is no AD sites available in SCCM boundaries.
$sites = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites
$CMBoundary = Get-CMBoundary | select value
foreach ($adsite in $sites.name) {
foreach ($cmb in $CMBoundary.value ) {
if (($cmb | select value) -eq ($adsite | select name)) {
"$adsite available in CM"
}
else { "$adsite is NOT in CM $cmb" }
}
}
Could someone please help me on this.
Think I see the issue.
On line 3, you're selecting the .Name property from $sites and storing it in $adsite, for each step of your loop.
foreach ($adsite in $sites.name) {
However, you then also attempt to take the .Name property again, with this line:
if (($cmb | select value) -eq ($adsite | select name))
This won't work. You've set $adsite to be equal to whatever was in $sites.Name, but that doesn't give a .Name property to $adsite.
Try this again, this time with the second Select statement removed. The reason this was failing is that there wouldn't be anything to compare against. I've revised your code to remove this logic, let me know if it works.
foreach ($adsite in $sites.name) {
foreach ($cmb in $CMBoundary.value ) {
if ($cmb -eq $adsite) {
"$adsite available in CM"
}
else { "$adsite is NOT in CM $cmb" }
}
}
Be warned, however, that if your boundary name doesn't exactly match an AD Site name, that this code will give you a lot of false positives.

Dynamically pass parameters to DSC resource?

I was working on a composite resource when I came across the issue of being able to dynamically pass parameters to a DSC resource and wondered if there's another way to tackle this that I'm missing.
Basically, I have the an array (Below) that contains one PSObject per desired file-share. Each of these PSObjects has properties that are used as parameters for my DSC resource (In this case the cLocalFileShare community resource).
The issue is, not all of these objects have all of the parameters defined. For example, some of my shares don't have any users/groups assigned to the ReadAccess permission, but in my ForEach loop (Below), a $null value is being passed to the actual resource as this permissions isn't defined, and this causes the resource to error as it is trying to set ReadAccess permissions to user $null.
My issue is, how do I tackle this - for this resource and others?
I've tried splatting the parameters in the DSC resource, but this doesn't seem to be supported. If this worked, I could build a different parameter list and pass that.
Somebody on Reddit suggested passing a string that contained all of the parameters, but again this doesn't seem to be supported.
My worst fear is, I will have to edit each resource to support (and ultimately ignore) $null values which seems like a really bad way to tackle this.
So, here's my array containing a PSObject per file share.
$MyConfig = #(
#{
Path = 'D:\Shares\Accounting'
Name = 'Accounting'
Ensure = 'Present'
ChangeAccess = 'AccountingAdmins'
ReadAccess = 'AccountingInterns,FinanceDepartment'
}
#{
Path = 'D:\Shares\Software'
Name = 'Software$'
Ensure = 'Present'
ReadAccess = 'DomainUsers'
}
)
Now, within the actual DSC configuration...
configuration {
ForEach ($ShareProperties in $MyConfig) {
# Each resource is named after the Path specified, but with the colon replaced as that's not valid character for the resource name
cLocalFileShare $ShareProperties.Path.Replace(':','__') {
Path = $ShareProperties.Path
Name = $ShareProperties.Name
Ensure = $ShareProperties.Ensure
ChangeAccess = $ShareProperties.ChangeAccess
ReadAccess = $ShareProperties.ReadAccess
}
}
}
Not the most elegant solution. But you could use a conditional section within your ForEach loop.
configuration {
ForEach ($ShareProperties in $MyConfig) {
# Each resource is named after the Path specified, but with the colon replaced as that's not valid character for the resource name
if ($ShareProperties.ChangeAccess -eq $null) {
cLocalFileShare $ShareProperties.Path.Replace(':','__') {
Path = $ShareProperties.Path
Name = $ShareProperties.Name
Ensure = $ShareProperties.Ensure
ReadAccess = $ShareProperties.ReadAccess
}
}
else
{
cLocalFileShare $ShareProperties.Path.Replace(':','__') {
Path = $ShareProperties.Path
Name = $ShareProperties.Name
Ensure = $ShareProperties.Ensure
ChangeAccess = $ShareProperties.ChangeAccess
ReadAccess = $ShareProperties.ReadAccess
}
}
}
}