Certificate Information Exported to CSV is Empty using Different Credential - arrays

I have an array that I write certificate information into using a credential called $array_otheruser but when I try to Export-Csv, the file becomes empty.
However, it writes fine if my export line is within the $ScriptBlock. However, I want to export it out of scriptblock, is this possible?
The code below writes an empty file:
[string][ValidateNotNullOrEmpty()]$passwd = "password"
$secpasswd = ConvertTo-SecureString -String $passwd -AsPlainText -Force
$mycreds = New-Object Management.Automation.PSCredential ("username", $secpasswd)
$array_otheruser = #()
$CSVFile = "c:\temp\temp9.csv"
$Certs = Get-ChildItem Cert: -Recurse
[ScriptBlock]$ScriptBlock = {
$Certs | where {
$_.PsIsContainer -eq $false
} | ForEach-Object ({
$obj | Add-Member -MemberType NoteProperty -Name "NotAfter" -Value $_.NotAfter
$obj | Add-Member -MemberType NoteProperty -Name "NotBefore" -Value $_.NotBefore
$array_otheruser += $obj
$obj = $null
})
}
$otheruser = Invoke-Command -ScriptBlock $ScriptBlock -ComputerName $env:computername -Credential $mycreds | Wait-Job
$otheruser | Receive-Job
$array_otheruser | Export-Csv -Path $CSVFile -NoTypeInformation
The code below writes into the file (where I've put the Export-Csv inside the scriptblock):
[ScriptBlock]$ScriptBlock = {
...
$array_otheruser += $obj
$obj = $null
})
##########This is the key difference, the export line is inside the ScriptBlock
$array_otheruser | Export-Csv -Path "c:\temp\temp9.csv" -NoTypeInformation
########
}
$otheruser = Invoke-Command -ScriptBlock $ScriptBlock -ComputerName $env:computername -Credential $mycreds | Wait-Job
$otheruser | Receive-Job
I would like to know if it is possible to have the Export-Csv file line outside of the scriptblock and have the cert information contents populate as it should using a different credential.

Related

Get a list of all groups in Azure for al members with a specific company

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

PowerShell: Create Hashtable with three columns

Ultimately, I'm trying to create a script that will get all Windows Services Running as a domain Service Account from a list of remote machines and output a csv file with three columns: the Service Account Name, the Windows Service, and the Hostname. I cannot figure out how to create the hashtable with two arrays. I've had some success with just one key and one array using += but even that has some issues and I'm reading this is inefficient.
This is modified code that gets all Win Services running as System on my local system:
$server = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$GetServiceAccounts = {
[CmdletBinding()]
param(
$hostname
)
$serviceList = #( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop )
$serviceList
}
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
$account = $service.StartName
$winService = $service.Name
$occurance = $service.SystemName
}
}
}
}
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
Process-CompletedJobs
Here is what I've tried that isn't working:
$server = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$serviceAccounts = #{}
$accountTable = #()
$winSvcTable = #()
$occurTable = #()
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$GetServiceAccounts = {
[CmdletBinding()]
param(
$hostname
)
$serviceList = #( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop )
$serviceList
}
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
$account = $service.StartName
$winService = $service.Name
$occurance = $service.SystemName
$script:serviceAccounts.Item($account) += $winService
$script:serviceAccounts.Item($account) += $occurance
}
}
}
}
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
Process-CompletedJobs
ForEach ($serviceAccount in $serviceAccounts.Keys) {
ForEach ($occurance in $serviceAccounts.Item($serviceAccount)) {
ForEach ($winService in $serviceAccounts.Item($serviceAccount)) {
$row = New-Object PSObject
Add-Member -InputObject $row -MemberType NoteProperty -Name "Account" -Value $serviceAccount
Add-Member -InputObject $row -MemberType NoteProperty -Name "Service" -Value $winService
Add-Member -InputObject $row -MemberType NoteProperty -Name "Hostname" -Value $occurance
$accountTable += $row
}
}
}
$accountTable | Export-Csv $reportCsv
I'm trying to modify code written by Andrea Fortuna that almost does what I want but want to split the second column into two. Again, I'm also looking for how to do this without adding to each array using += if possible. https://www.andreafortuna.org/2020/03/25/windows-service-accounts-enumeration-using-powershell/
If your goal is to export to CSV, then a single top-level hashtable is not the data structure you want.
Export-Csv will expect a collection of individual objects, so that's what you'll want to create:
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
# don't assign this new object to anything - let it "bubble up" as output instead
[pscustomobject]#{
Account = $service.StartName
Service = $service.Name
Occurrence = $service.SystemName
}
}
}
}
}
Now you can do:
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
Process-CompletedJobs |Export-Csv ...
What about this?
$server = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$GetServiceAccounts = {
[CmdletBinding()]
param(
$hostname
)
$serviceList = #( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop )
$serviceList
}
Function Process-CompletedJobs(){
$jobs = Get-Job -State Completed
$hashtable = #{}
ForEach ($job in $jobs) {
$data = Receive-Job $job
Remove-Job $job
If ($data.GetType() -eq [System.Object[]]) {
$serviceList = $data | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
ForEach ($service in $serviceList) {
$account = $service.StartName
$winService = $service.Name
$occurance = $service.SystemName
$hashtable[$account] = #{winService = $winService; occurance = $occurance}
}
}
}
return $hashtable
}
Start-Job -ScriptBlock $GetServiceAccounts -Name "read_$($server)" -ArgumentList $server | Wait-Job > $null
$myHashTable = Process-CompletedJobs
Make it simple it will work, this is the complete script and I have tried and valid for many servers you can change the variable $hostnames = $env:COMPUTERNAME,host2,host3,.. as you need
and I added some parameters to get a grid view of result to test and add force and notypeinfo. in export-csv
Here is the code - I hope you mark it answer if it helps:
$hostnames = $env:COMPUTERNAME
$tgtAcct = 'SYSTEM'
$reportCsv = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath ("report.$(Get-Date -Format `"yyyMMdd_hhmmss`").csv")
$TableName = "System Accounts"
#Create a table
$Table = new-object System.Data.DataTable "$TableName"
#Create a column and you can increase it as many as you need
$col1 = New-Object System.Data.DataColumn "Service Account Name",([string])
$col2 = New-Object System.Data.DataColumn "Windows Service",([string])
$col3 = New-Object System.Data.DataColumn "Hostname",([string])
# Add the Columns
$Table.columns.add($col1)
$Table.columns.add($col2)
$Table.columns.add($col3)
foreach($hostname in $hostnames){
$serviceList = Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name, StartName, SystemName -ErrorAction Stop | Where-Object { $_.StartName -ne $null -and $_.StartName.ToUpper().Contains($tgtAcct) }
foreach ($service in $serviceList){
$Row = $Table.NewRow()
$Row."Hostname" = $hostname
$Row."Service Account Name" = $service.StartName
$servicename = $service.Name.ToString()
$Row."Windows Service" = $servicename
$Table.Rows.Add($Row)
}}
$Table | Out-GridView
$Table | Export-Csv $reportCsv -Force -NoTypeInformation

How to create an array of objects with different values for each

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
}
}

Match rows from two different arrays

I'm trying to create a list of all servers in the domain and note which ones are being backed up. I'm using a Quest plugin for the AD server list and a Veeam plugin for the back up list. The arrays being created, but I'm having trouble understanding how to compare and create the final list.
# Get list of all servers in AD
if ((Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin Quest.ActiveRoles.ADManagement
}
$servers = get-qadcomputer -SizeLimit 0 | where {$_.OSName -match "Server"}
$arrAllServers = #()
foreach ($server in $servers) {
$singleserver = New-Object PSObject
$singleserver | Add-Member -Name "Name" -Value $x.name -MemberType NoteProperty
$arrAllServers += $singleserver
}
# Get list of servers being backed up
if ((Get-PSSnapin -Name VeeamPSSnapin -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin VeeamPSSnapin
}
Connect-VBRServer -Server host.foo.org -Port 9392
$allDetails = #()
$jobs = Get-VBRJob | ?{$_.JobType -eq "Backup"}
# Loop through each job adding details to array
foreach ($job in $jobs) {
$Object = Get-VBRJobObject -Job $job
$jobOptions = New-Object PSObject
$jobOptions | Add-Member -MemberType NoteProperty -Name "Name" -value $job.name
$jobOptions | Add-Member -MemberType NoteProperty -Name "Enabled" -value $job.isscheduleenabled
$jobOptions | Add-Member -MemberType NoteProperty -Name "Servers" -Value (#($Object.name) | Out-String).Trim()
$jobOptions | Add-Member -MemberType NoteProperty -Name "Backup Mode" -value $job.backuptargetoptions.algorithm
$repo = (Get-VBRBackupRepository | ?{$_.HostId -eq $job.TargetHostId -and $_.Path -eq $job.TargetDir}).name
$jobOptions | Add-Member -MemberType NoteProperty -Name "Repository" -value $repo
$allBackupDetails += $jobOptions
}
Disconnect-VBRServer
# Now match by server name and produce some output.
foreach ($lserver in $arrAllServers) {
foreach ($bserver in $allDetails) {
if($lserver.name -eq $bserver.servers) {
Write-Host $lserver.name $bserver.name
}
}
}
You create the array $allDetails and read from it, but you write the objects to $allBackupDetails
$bserver.servers is an array so you need to use -contains or -in
$bserver.servers might be "broken" because of your out-string-operation (why use it?)
$arrAllServers is wasted resources when you only want the servername.
Try:
# Load modules
if ((Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin Quest.ActiveRoles.ADManagement
}
if ((Get-PSSnapin -Name VeeamPSSnapin -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin VeeamPSSnapin
}
# Get list of all servers in AD
$servers = get-qadcomputer -SizeLimit 0 | where {$_.OSName -match "Server"} | Select-Object -ExpandProperty Name
# Get backup jobs
Connect-VBRServer -Server host.foo.org -Port 9392
$allBackupDetails = #()
$jobs = Get-VBRJob | ?{$_.JobType -eq "Backup"}
# Loop through each job adding details to array
foreach ($job in $jobs) {
$Object = Get-VBRJobObject -Job $job
$jobOptions = New-Object PSObject
$jobOptions | Add-Member -MemberType NoteProperty -Name "Name" -value $job.name
$jobOptions | Add-Member -MemberType NoteProperty -Name "Enabled" -value $job.isscheduleenabled
$jobOptions | Add-Member -MemberType NoteProperty -Name "Servers" -Value #($Object.name)
$jobOptions | Add-Member -MemberType NoteProperty -Name "Backup Mode" -value $job.backuptargetoptions.algorithm
$repo = (Get-VBRBackupRepository | ?{$_.HostId -eq $job.TargetHostId -and $_.Path -eq $job.TargetDir}).name
$jobOptions | Add-Member -MemberType NoteProperty -Name "Repository" -value $repo
$allBackupDetails += $jobOptions
}
Disconnect-VBRServer
# Now match by server name and produce some output.
foreach ($server in $servers) {
foreach ($backupDetails in $allBackupDetails) {
if($backupDetails.servers -contains $server) {
Write-Host "$server is in $($backupDetails.name)"
}
}
}

Cannont Add-Member as the Member with the Name Exists

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

Resources