I'm trying to recurse an NTFS folder structure, and output a CSV file that only displays each USER account with permissions on only the folders. Everything in the script outputs correctly EXCEPT for the portion that discovers a group and proceeds to enumerate the users in that group using Get-ADGroupMember. While debugging, I can see that each user within the group (even with nested groups) is outputted, but I guess I'm not properly "arraying" each output of the command and sending it onward to my "out" array.
I marked the section I'm having trouble with. Any help folks could provide would be very much appreciated. Thanks!
$Answer = Read-Host 'Do you wish to use an answer file? File must be named answer.csv and must reside in same directory as script. (Default is [N])'
If ($Answer -eq "y") {
$AnsFile = Import-Csv answer.csv | Select src,outdir,domain,user,pwd
$List_Dir = $AnsFile.src
$OutPath = $AnsFile.outdir
$DomainName = $AnsFile.domain
$Admin = $AnsFile.user
$Pwd = $AnsFile.pwd
}
Else {
Do {
$List_Dir = Read-Host 'Enter the directory path to be searched/recursed'
$TestList_Dir = Test-Path $List_Dir
If ($TestList_Dir -eq $True) {Write-Host "List directory checks out..."}
Else {Write-Host "Incorrect source directory. Please try again." -foregroundcolor red -backgroundcolor yellow}
}
While ($TestList_Dir -eq $False)
Do {
$OutPath = Read-Host 'Enter the directory path where the output files will be saved. Do not add a trailing slash.'
$TestOutPath = Test-Path $OutPath
If ($TestOutPath -eq $True) {Write-Host "Output path checks out..."}
Else {Write-Host "Incorrect output path. Please try again." -foregroundcolor red -backgroundcolor yellow}
}
While ($TestOutPath -eq $False)
$DomainName = Read-Host 'Enter the non-distinguished name of the Active Directory domain'
$Admin = Read-Host 'Type in an administrative account with rights to read AD Security Groups'
$Pwd = Read-Host 'Enter the adminstrative account password'
}
$Folder_Array = #()
write-host "List directory = $List_Dir"
write-host "Output path = $OutPath"
write-host "Domain = $DomainName"
write-host "Admin account = $Admin"
write-host "Password = $Pwd"
Import-Module ActiveDirectory
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$CType = [DirectoryServices.AccountManagement.ContextType]::Domain
$IDType = [DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$DomainContext = New-Object DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $CType, $DomainName, $Admin, $Pwd
#$pat = "^[a-zA-Z0-9_:.]+$"
$pat = "^[a-zA-Z0-9_:.\]+$]"
get-childitem $List_Dir -recurse | where-object {$_.psIsContainer -eq $true} | foreach-object {
$a = ($_.FullName)
$d = $a -match $pat
$e = (get-acl $_.FullName).Access
foreach ($e1 in $e) {
$f = $e1.FileSystemRights
$g = $e1.AccessControlType
$SecID = $e1.IdentityReference
foreach ($Sec in $SecID) {
$GroupPrincipal = [DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($DomainContext, $IDType, $Sec)
if ($GroupPrincipal -ne $null) {
$Sec = $Sec.ToString()
$Sec = $Sec.Split("\")[1]
Get-AdGroupMember $Sec -Recursive | ForEach-Object {
$User = ($_.SamAccountName)
foreach ($u in $User) {
$out = new-object psobject
$out | add-member noteproperty Path $a
$out | add-member noteproperty Unix_Safe $d
$out | Add-Member NoteProperty UserAccount $u
$out | add-member noteproperty Permission $f
$out | add-member noteproperty AccessType $g
$Folder_Array += $out
}
}
}
else {
$e2 = $Sec.ToString()
$e2 = $e2.split("\")[1]
$out = new-object psobject
$out | add-member noteproperty Path $a
$out | add-member noteproperty Unix_Safe $d
$out | Add-Member NoteProperty UserAccount $e2
$out | add-member noteproperty Permission $f
$out | add-member noteproperty AccessType $g
$Folder_Array += $out
}
}
}
}
$Folder_Array | Select Path,UserAccount,Permission,AccessType,Unix_Safe | Export-Csv "$OutPath\folderonly.csv" -NoTypeInformation
The problem isn't so much with how you're doing it, it's more of when you're doing things. Let me explain...
Get-AdGroupMember $Sec -Recursive | ForEach-Object {
$User = ($_.SamAccountName)
foreach ($u in $User) {
$e2 = $u
}
}
}
****************************************************
else {
$e2 = $Sec.ToString()
$e2 = $e2.split("\")[1]
}
}
}
$out = new-object psobject
$out | add-member noteproperty Path $a
$out | add-member noteproperty Unix_Safe $d
$out | Add-Member NoteProperty UserAccount $e2
$out | add-member noteproperty Permission $f
$out | add-member noteproperty AccessType $g
$Folder_Array += $out
Given that, if it is a group you are taking all users for the group and setting that array of users to $User, and then going through that array, and assigning each user, one at a time, to $e2. Once you're done with that you create your object, and add that object to the array for output.
Let's say that group has 3 users in it, Tom, Dick, and Harvey (Harry was busy, he sent his brother instead). So now:
$User = #("Tom","Dick","Harvey")
Then you cycle through that assigning each to $e2, which basically comes out to this (some pseudocode here):
If(is a group){
$User = Get-ADGroup |select -expand samaccountname
ForEach($u in $User){
$e2 = "Tom"
<Next item in array>
$e2 = "Dick"
<next item in array>
$e2 = "Harvey"
<No more items in array, end ForEach>
So now when it moves on to create your object $e2 = "Harvey" and Tom and Dick are just out of luck. To resolve that we have options. Either:
A) Move object creation to inside the If/Else portions of the loop, specifically to create an object every time you assign $e2, and add those objects to the output array immediately after making them.
or:
B) Make $e2 an array by changing all references to setting it to read either $e2 += $u or $e2 = ,$Sec.ToString().Split("\")[1]. And then when you create objects do that like:
ForEach($User in $e2){
$Folder_Array += [PSCustomObject][Ordered]#{
'Path' = $a
'Unix_Safe' = $d
'UserAccount' = $User
'Permission' = $f
'AccessType' = $g
}
}
Related
I need just a list of all groups (just the group names and desciption, not the users) that anyone who part of Company A (field in AD) is part of the company. This is what I have, but can't get it to work right. Any help?
$UserList = #()
$FinalGroupList = #()
$UserList = Get-Recipient -Filter {Company -eq "Company"} | where-object {$_.RecipientType -eq "UserMailbox"}
foreach ($user in $UserList) {
$CurrentUserGroups = Get-AzureADUser -User $user.PrimarySmtpAddress | Get-AzureADUserMembership
foreach ($group in $CurrentUserGroups) {
if ($FinalGroupList.displayname -notcontains $group.displayname) {
$FinalGroupList = $FinalGroupList + $group
}
}
}
foreach ($FinalListItem in $FinalGroupList) {
$CSVData = New-Object psobject
$CSVData | Add-Member -MemberType NoteProperty -name DisplayName -Value $FinalListItem.DisplayName
$CSVData | Add-Member -MemberType NoteProperty -name Description -Value $FinalListItem.Description
$CSVData | export-csv c:\temp\Groups.csv -Append -NoTypeInformation
}
I have tried in my environment with below powershell commands to get the group and its description whose users are present in specific company and could successfully get the same.
#Get-AzureADUser -All $true | Select-Object -Property CompanyName, UserPrincipalName
#$companyUsers =Get-AzureADUser | ?{ $_.CompanyName -eq '<that specific company name>' }
$companyname=”SamComp” //this the specific company name I am looking for
$companyUsers =Get-AzureADUser | ?{ $_.CompanyName -eq 'SamComp' }
foreach ($user in $companyUsers) {
Get-AzureADUserMembership -ObjectId $user.ObjectId
$groups = $user | Get-AzureADUserMembership
$list | Sort-Object DisplayName -Unique | fl -GroupBy DisplayName -Property Description
$list
}
OUTPUT:
Reference: Get-AzureADUserMembership (AzureAD) | Microsoft Docs
I'm new to programming.
I want to create an array of 4 objects with different values for each object, without overwriting the previous one.
This is my code:
$WKey = "hkcu:\Software\Microsoft\Windows\CurrentVersion\Uninstall\"
$keys = get-childitem -path hkcu:\Software\Microsoft\Windows\CurrentVersion\Uninstall\
$IN = #{}
foreach ($key in $keys.pschildname){
$name = Get-ItemPropertyValue -Path $WKey$key -name displayname
if ($name -like '*injaz*'){
$HM = #{}
$HM.Name = Get-ItemPropertyValue -Path $WKey$key -name displayname
$HM.Version = Get-ItemPropertyValue -Path $WKey$key -name displayVersion
$HM.Uninstaller = Get-ItemPropertyValue -Path $WKey$key -name UninstallString
$HM.Keyname = $key
$Objectname = New-Object PSobject -Property $HM
$IN.add($Objectname.Name,$Objectname.Version,$Objectname.Uninstaller,$Objectname.keyname)
$Objectname
$IN
}
}
You need to create a pscustom object and add the properties to it.
$WKey = "hkcu:\Software\Microsoft\Windows\CurrentVersion\Uninstall\"
$keys = get-childitem -path hkcu:\Software\Microsoft\Windows\CurrentVersion\Uninstall\
$IN = [System.Collections.ArrayList]#{}
foreach ($key in $keys.pschildname){
$name = Get-ItemPropertyValue -Path $WKey$key -name displayname
if ($name -like '*injaz*'){
#Create object
$HM = New-Object -TypeName psobject
#Add properties to object
$HM | Add-Member -MemberType NoteProperty -Name "Name" -Value $(Get-ItemPropertyValue -Path $WKey$key -name displayname)
$HM | Add-Member -MemberType NoteProperty -Name "Version" -Value $(Get-ItemPropertyValue -Path $WKey$key -name displayVersion)
#Continue with the other values
#Add object to array
$IN.Add($HM) | Out-Null
}
}
I want to get data from 2 commands and combine them into 1 object.
My goal is to get usage and cost of the Partner center. To do this I got the usage and cost with the command Get-PartnerCustomerSubscriptionUsage of the Partner center module. But when i Retrieve information of my customer/subscription I got the ugly ResourceID inside my report. And this is not presentable. This is why I need to get the real ResourceName, not the Resource name that is provided with the Get-PartnerCustomerSubscriptionUsage command.
After some digging in the documentation I got an idea to retrieve the ResourceUri that has the entire uri like /subscription/xxxxx/Resourcegroup/xxxx/ms.vm/The name that i want in my report. The command that has this value is : Get-PartnerCustomerSubscriptionUtilization. So I guessed that I just ditch the Get-PartnerCustomerSubscriptionUsage and use the PartnerCustomerSubscriptionUtilization instead , but this one does not have the totalcost per azure Resource.
Oké hang on with me the problem is getting there.
So now I created a Powershell script that will run the both commands , and combine them inside an Powershell object that will be exported to a csv. I can get one command running , providing the info from that command object into my custom object that is created and export it to csv. The problem is starting when I want to combine the both.
$Customers= Get-PartnerCustomer
for ($i=0 ; $i -lt $Customers.length; $i ++){
$subscription = Get-PartnerCustomerSubscription -CustomerId $Customers[$i].CustomerId
for ($j=0 ; $j -lt $subscription.length; $j ++){
if ( $subscription[$j].UnitType -eq "Usage-based" )
{
#Create title in csv
$customerId = $Customers[$i].CustomerId
$customerName= $Customers[$i].Name
$subscriptionId = $subscription[$j].SubscriptionId
$subscriptionName = $subscription[$j].OfferName
$usage = Get-PartnerCustomerSubscriptionUsage -CustomerId $customerId -SubscriptionId $subscriptionId
#new object for the export excel
$ExportExcel = New-Object -TypeName PSObject
$array = New-Object -TypeName PSObject
$End= (get-date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss-08:00")
$Start = (Get-Date).AddDays(-1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss-08:00")
$util = Get-PartnerCustomerSubscriptionUtilization -CustomerId $customerId -SubscriptionId $subscriptionId -StartDate $Start -EndDate $End -ShowDetails
for ($y=0 ; $y -lt $util.length; $y ++)
{
$array = [PSCustomObject][ordered]#{
"ResourceID"=$util[$y].Id
"ResourceName"=$util[$y].ResourceUri.OriginalString
}
}
for ($z=0 ; $z -lt $usage.length; $z ++)
{
$LastModifiedDate = $usage[$z].LastModifiedDate.DateTime.ToString("yyyy-MM-dd")
if ( $LastModifiedDate -ge $Lastdate )
{
if ($usage[$z].ResourceId -eq $array[$z].ResourceID){
#Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "Category" -Value $array[$z].ResourceName -Force
**echo $array[$z].ResourceName**
}
Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "Category" -Value $usage[$z].Category -Force
Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "QuantityUsed" -Value $usage[$z].QuantityUsed -Force
Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "ResourceId" -Value $usage[$z].ResourceId -Force
}
}
$ExportExcel | Export-Csv –append -Path "C:\$customername.csv" -NoTypeInformation
}
}
}
As you can see I've read everything from one command inside an object and then loop over the other one. Once the resourceID is equal over the both commands, I need to add it to the Object. (for testing I just test this with an echo)
The echo with the ***** doesn't print anything. So I can't get any Resourcename inside my csv. Does anyone have a clue what I am doing wrong. Where the combination of the 2 objects fail ?
Just for grins, I've made a few edits. Can you let me know if this improves the situation?
$Customers= Get-PartnerCustomer
for ($i=0 ; $i -lt $Customers.length; $i ++){
$subscription = Get-PartnerCustomerSubscription -CustomerId $Customers[$i].CustomerId
for ($j=0 ; $j -lt $subscription.length; $j ++){
if ( $subscription[$j].UnitType -eq "Usage-based" )
{
#Create title in csv
$customerId = $Customers[$i].CustomerId
$customerName= $Customers[$i].Name
$subscriptionId = $subscription[$j].SubscriptionId
$subscriptionName = $subscription[$j].OfferName
$usage = Get-PartnerCustomerSubscriptionUsage -CustomerId $customerId -SubscriptionId $subscriptionId
#new object for the export excel
$ExportExcel = New-Object -TypeName PSObject
$array = #()
$End= (get-date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss-08:00")
$Start = (Get-Date).AddDays(-1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss-08:00")
$util = Get-PartnerCustomerSubscriptionUtilization -CustomerId $customerId -SubscriptionId $subscriptionId -StartDate $Start -EndDate $End -ShowDetails
for ($y=0 ; $y -lt $util.length; $y ++)
{
$array += [PSCustomObject][ordered]#{
"ResourceID"=$util[$y].Id
"ResourceName"=$util[$y].ResourceUri.OriginalString
}
}
for ($z=0 ; $z -lt $usage.length; $z ++)
{
$ExportExcel = New-Object -TypeName PSObject
$LastModifiedDate = $usage[$z].LastModifiedDate.DateTime.ToString("yyyy-MM-dd")
if ( $LastModifiedDate -ge $Lastdate )
{
if ($usage[$z].ResourceId -eq $array[$z].ResourceID){
#Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "Category" -Value $array[$z].ResourceName -Force
**echo $array[$z].ResourceName**
}
Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "Category" -Value $usage[$z].Category -Force
Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "QuantityUsed" -Value $usage[$z].QuantityUsed -Force
Add-Member -InputObject $ExportExcel -MemberType NoteProperty -Name "ResourceId" -Value $usage[$z].ResourceId -Force
$ExportExcel | Export-Csv –append -Path "C:\$customername.csv" -NoTypeInformation
}
}
}
}
}
I edited the line below to retain all values for each iteration of loop $y.
$array += [PSCustomObject][ordered]#{
"ResourceID"=$util[$y].Id
"ResourceName"=$util[$y].ResourceUri.OriginalString
}
I added the line below so that a new $ExportExcel object could be created to accept your new property additions during loop $z.
$ExportExcel = New-Object -TypeName PSObject
I moved the CSV export line to inside of the $z loop so that each iteration of setting properties for $ExportExcel could be captured.
Edit: Rephrasing question to be more clear
Here is the working code:
$arr = #(
#('prop0','name0'),
#('prop1','name1'),
#('prop2','name2')
)
$obj = New-Object PsObject
foreach($innerArr in $arr)
{
$obj | Add-Member -NotePropertyName $innerArr[0] -NotePropertyValue $null
}
$obj2 = New-Object PsObject
$count = 0
foreach($innerArr in $arr)
{
$value = 'val' + $count
$obj2 | Add-Member -NotePropertyName $innerArr[1] -NotePropertyValue $value
$count ++
}
for($i=0; $i -lt $arr.Count; $i++)
{
# This is what I want to consolidate into one line
# {
$prop_name = $arr[$i][1]
$obj | Add-Member -NotePropertyName $arr[$i][1] -NotePropertyValue $obj2.$prop_name
# }
}
How do I do this without assigning the name of the property to $prop_name?
The output of $obj should look like this:
PS C:\> $obj
prop0 :
prop1 :
prop2 :
name0 : val0
name1 : val1
name2 : val2
This will get you past the part you are erroring on :
$obj | Add-Member -NotePropertyName $arr[$i][1] -NotePropertyValue $obj2.PSObject.Properties.Item("name$i").value
The issue is your treating the PSobject like a array when it doesnt act like a array. Its more along the lines of a dictionary.
Key : Value
Key : Value
Key : Value
Since there is no hierarchy you have to specify which Key you are looking for.
If I was going to do it i would go with something like
$obj2arr = #($obj2.PSObject.Properties | %{$_})
for($i=0; $i -lt $arr.Count; $i++)
{
$obj | Add-Member -NotePropertyName $arr[$i][1] -NotePropertyValue $obj2arr[$i].Value
}
Turning the PSObject into an array of Items and puling the property I want to use
Since your question has changed a little since i answered here is the script shortened
$arr = #(
#('prop0','name0'),
#('prop1','name1'),
#('prop2','name2')
)
$obj = New-Object PsObject
$arr | %{ $obj | Add-Member -NotePropertyName $_[0] -NotePropertyValue $null}
$obj2 = New-Object PsObject
$count = 0
$arr | %{ $obj2 | Add-Member -NotePropertyName $_[1] -NotePropertyValue ('val' + $count); $count++}
$obj2arr = #($obj2.PSObject.Properties | %{$_})
$count = 0
$arr | foreach-object{$obj | Add-Member -NotePropertyName ($_)[1] -NotePropertyValue $obj2arr[$count].Value; $count++}
$obj
Can someone help me out with getting the disk info? I have 3 disks but I'm unable to get their information using add member.
I get an error:
"Add-Member : Cannot add a member with the name "Disks" because a member with that name already exists. If you want to overwrite the member anyway, use the Force parameter to overwrite it."
This is my code:
function Get-Inven {
param([string[]]$computername)
#Import-Module ActiveDirectory
foreach ($computer in $computername) {
$disks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $computer -Filter 'DriveType=3'
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer
#$comp = Get-ADComputer -Filter { cn=$computer }
$info = #{
'ComputerName'=$computer;
'OSVersion'=$os.caption;
'DnsHostName'=$comp.dnshostname
}
$obj = New-Object -TypeName PSObject -Property $info
foreach ($disk in $disks) {
$info = #{
'DriveLetter'=$disk.deviceID;
'FreeSpace'=($disk.freespace / 1MB -as [int])
}
$diskobj = New-Object -TypeName PSObject -Property $Info
$obj | Add-Member -MemberType NoteProperty -Name Disks -Value $diskobj
}
}
}
You can still set the Name property if you add the -Force parameter. You should also add the -PassThru switch parameter to emit the object back to the pipeline:
$obj | Add-Member -MemberType NoteProperty -Name Disks -Value $diskobj -Force -PassThru
UPDATE:
In my opinion you can simplify the function (no add-member calls):
foreach ($computer in $computername) {
$disks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $computer -Filter 'DriveType=3'
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer
#$comp = Get-ADComputer -Filter { cn=$computer }
$info = #{
ComputerName=$computer
OSVersion=$os.caption
DnsHostName=$comp.dnshostname
FreeSpaceMB= ($disks | foreach { "{0},{1:N0}" -f $_.Caption,($_.freespace/1MB) }) -join ';'
}
New-Object -TypeName PSObject -Property $info
}
Since there are multiple disks, you need to create the disk property as an array, then add each disk to the array. Also, remember to output $obj at the end of the $computername foreach.
function Get-Inven {
param([string[]]$computername)
$computername = 'localhost'
foreach ($computer in $computername) {
$disks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $computer -Filter 'DriveType=3'
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer
#$comp = Get-ADComputer -Filter { cn=$computer }
$info = #{
'ComputerName'=$computer;
'OSVersion'=$os.caption;
'DnsHostName'=$comp.dnshostname
}
$obj = New-Object -TypeName PSObject -Property $info
$obj | Add-Member -MemberType NoteProperty -Name Disks -Value #()
$obj | Add-Member -MemberType ScriptProperty -Name DisksList -Value {
($this.Disks|%{$_.DriveLetter + ',' + $_.FreeSpace}) -join ';'
}
foreach ($disk in $disks) {
$info = #{
'DriveLetter'=$disk.deviceID;
'FreeSpace'=($disk.freespace / 1MB -as [int])
}
$diskobj = New-Object -TypeName PSObject -Property $Info
$obj.Disks += $diskobj
}
$obj
}
}
$result = get-inven localhost
$result| select "OSVersion","DnsHostName","ComputerName","DisksList"|ConvertTo-Csv