Limiting Scope Powershell - powershell

So i've been playing around with this script to audit file server folder ACL's, and it works great, just returning too much data. What i was hoping i could do is filter out the folders where there are no special permissions than the parent folder, I think this would help me reduce the amount of entries into my table and allow me to manipulate it into something more readable. Basically only return the parent ACL and any child object that doesn't inherit from parent or has special permissions.
$connectionString = "Server=KCC-RPT-Admin\SQLEXPRESS;Database=Audit;User ID=report_admin; Password=Payday!5;"
$tableName = "FilePermissions"
function WriteSQL ($query)
{
if ($debug -eq $true) {Write-Host $query}
$Connection = New-Object System.Data.SqlClient.SqlConnection
$Connection.ConnectionString = $connectionString
$Connection.Open()
$Command = New-Object System.Data.SqlClient.SqlCommand
$command.Connection = $Connection
$command.CommandText = $query
$command.ExecuteNonQuery()
$connection.Close()
}
$ErrorActionPreference = "Continue"
$strComputer = $env:ComputerName
$colDrives = Get-PSDrive -PSProvider Filesystem
ForEach ($DriveLetter in $colDrives) {
$StartPath = "E:\Share\"
Get-ChildItem -LiteralPath $StartPath -Recurse -Directory |
ForEach {
$FullPath = Get-Item -LiteralPath (Get-Item -LiteralPath $_.PSPath)
(Get-Item -LiteralPath $FullPath).GetAccessControl() |
Select * -Expand Access |
Select #{N='ServerName';E={$strComputer}},
#{N='FullPath';E={$FullPath}},
#{N='Type';E={If($FullPath.PSIsContainer -eq $True) {'D'} Else {'F'}}},
#{N='Owner';E={$_.Owner}},
#{N='Trustee';E={$_.IdentityReference}},
#{N='Inherited';E={$_.IsInherited}},
#{N='InheritanceFlags';E={$_.InheritanceFlags}},
#{N='AceFlag';E={$_.PropagationFlags}},
#{N='AceType';E={$_.AccessControlType}},
#{N='AccessMasks';E={$_.FileSystemRights}} } |
%{
$query = "INSERT INTO $tableName (servername,fullpath,type,owner,trustee,inherited,inheritanceflags,aceflag,acetype,accessmasks) VALUES ('$($_.servername)','$($_.fullpath)','$($_.type)','$($_.owner)','$($_.trustee)','$($_.inherited)','$($_.inheritanceflags)','$($_.aceflag)','$($_.acetype)','$($_.accessmasks)')"
WriteSQL $query
}
}

Related

How to Make my Powershell script discover a Desktop on my network and delete user profiles remotely

I am fairly new with powershell scripting and trying to learn. I am trying to create a PS1 file that runs with a GUI for easy use for other people on my team, that can delete profiles off of a Desktop/Laptop remotely. I have a script below that works for local profiles and would like to enhance it for remote PC's so we dont physically have to collect devices when cleaning up desktops/laptops that are filled with profiles. These computers have a program that runs that refreshes the profiles daily so a script that cleans based on days since last log in will not work.
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(400,300)
$form.Text = "Delete User Profiles"
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Size = New-Object System.Drawing.Size(360,200)
$listBox.Location = New-Object System.Drawing.Point(20,20)
$listBox.SelectionMode = [System.Windows.Forms.SelectionMode]::MultiSimple
$userProfiles = Get-ciminstance -classname Win32_UserProfile
foreach ($userProfile in $userProfiles) {
if (($userProfile.localpath -notmatch "Public") -and ($userProfile.localpath -notmatch "Default")) {
$listBox.Items.Add($userProfile.localpath)
}
}
$form.Controls.Add($listBox)
$deleteButton = New-Object System.Windows.Forms.Button
$deleteButton.Size = New-Object System.Drawing.Size(100,30)
$deleteButton.Location = New-Object System.Drawing.Point(20,230)
$deleteButton.Text = "Delete"
$deleteButton.Add_Click({
foreach ($item in $listBox.SelectedItems) {
##Remove-Item -Path "$item" -Recurse -Force
##Properly removing item from WMI. note: -whatif currently specified so the operation doesn't actually occur
get-ciminstance -classname win32_userprofile | where-object -property localpath -like $item | remove-ciminstance ##-whatif
}
[System.Windows.Forms.MessageBox]::Show("Done","Delete Dialog",[Windows.Forms.MessageBoxButtons]::OkCancel)
})
$form.Controls.Add($deleteButton)
#$form.Controls.Add($listBox)
$showButton = New-Object System.Windows.Forms.Button
$showButton.Size = New-Object System.Drawing.Size(100,30)
$showButton.Location = New-Object System.Drawing.Point(140,230)
$showButton.Text = "Refresh"# "Show"
$showButton.Add_Click({
# foreach ($item in $listBox.SelectedItems) {
# [System.Windows.Forms.MessageBox]::Show("You have chosen to remove $item","Profile Selection Dialog",[Windows.Forms.MessageBoxButtons]::OkCancel)
# }
$listBox.Items.Clear()
$userProfiles = Get-ciminstance -classname Win32_UserProfile
foreach ($userProfile in $userProfiles) {
if (($userProfile.localpath -notmatch "Public") -and ($userProfile.localpath -notmatch "Default")) {
$listBox.Items.Add($userProfile.localpath)
}
}
})
$form.Controls.Add($showButton)
$form.ShowDialog()
Please Help.........

Connection to OneDrive issue in PowerShell

I have this script running in VSCode but different $SiteURL. I open this script again in ISE and change the $SiteUR $searchfor and $folderpath. The issue I have is everytime I run in ISE and when it's doing Get-PnPListItem, it's getting an items from the path that I provided in the VSCode. Not sure what is going on so I would be really appreciated if I can get any help or suggestion.
$SiteURL = "https://companyName-my.sharepoint.com/personal/user_id"
$searchfor = "/personal/user_id/Documents/Pkmon"
$folderpath = "Documents/Pkmon"
$CSVFile = "C:\Users\user\Desktop\Resource\FolderStats.csv"
#Connect to SharePoint Online
Connect-PnPOnline $SiteURL -useWebLogin
$FolderItems = Get-PnpListItem -List $folderpath -PageSize 2000 -Fields ID, FileDirRef, FileRef -ScriptBlock `
{ Param($items) $global:counter += $items.Count; Write-Progress -PercentComplete ($global:Counter / ($List.ItemCount) * 100) -Activity `
"Getting Items from Folder '$FolderServerRelativeURL'" -Status "Getting Items $global:Counter of $($List.ItemCount)"; }
$fieldvalues = $FolderItems.Fieldvalues
$result = #()
foreach ($field in $fieldvalues) {
$obj = New-object psobject -property $field
$result += $obj.fileref
}
$final = $result | where-object {$_ -match $searchfor}
$item = New-Object psobject -Property #{
FolderName = Split-Path -Path $searchfor -Leaf
URL = $searchfor
filesfoldercount = $final.count
}
$item
$item | Export-Csv -Path $CSVFile -NoTypeInformation
You can capture the connection object in a variable like
$thisConnection = Connect-PnPOnline $SiteURL -useWebLogin -ReturnConnection
-ReturnConnection acts as what is normally called -PassThru. It makes the cmdlet return the connection object for use with the -Connection parameter on other cmdlets.
Then use that in parameter -Connection of the Get-PnpListItem cmdlet:
$FolderItems = Get-PnpListItem -List $folderpath -Connection $thisConnection ...
You may also need to specify the connection in the VSCode instance.
When done, disconnect using Disconnect-PnPOnline -Connection $thisConnection

PowerShell Exporting

Can someone point me in the right direction? Basically, I would like to export the results of my testpath to a csv. Below is what I am working with. I have read a couple Microsoft documents but they only seem to confuse me even more. Any feedback is appreciated.
$ComputerList = (Get-ADComputer -Filter *).name
$ComputerList
write-host "`n"
Foreach ($Computer in $ComputerList)
{
$userfolders = get-childitem "\\$Computer\C$\users\"
foreach ($user in $userfolders) {
$ErrorActionPreference= 'silentlycontinue'
$path = $user.fullname
write-host $path
$t = test-path -Path "$path\AppData\Local\Google\Chrome\User Data\Default"
IF ($t -eq 'True') {write-host "Has it" -ForegroundColor yellow} ELSE {write-host "no"}
write-host "`n"
}
$Output =New-Object -TypeName PSObject -Property #{
} | Select-Object
}
$Output | C:\Users\"user"\Chrome.csv
write-output "Script finished. Please check output files"
Assuming you want a record per user per computer, there's two things you want to change structurally:
Create new objects in the inner foreach loop
Assign all the objects created to $Output:
$ComputerList = (Get-ADComputer -Filter *).name
$ComputerList
write-host "`n"
$Output = Foreach ($Computer in $ComputerList) {
$userfolders = get-childitem "\\$Computer\C$\users\"
foreach ($user in $userfolders) {
$ErrorActionPreference = 'silentlycontinue'
$path = $user.fullname
write-host $path
$t = test-path -Path "$path\AppData\Local\Google\Chrome\User Data\Default"
IF ($t -eq 'True') {write-host "Has it" -ForegroundColor yellow} ELSE {write-host "no"}
write-host "`n"
New-Object -TypeName PSObject -Property #{
# We still need a bit of magic here
}
}
}
$Output | C:\Users\"user"\Chrome.csv
write-output "Script finished. Please check output files"
Now we just need to decide on what properties to add to our output objects:
New-Object -TypeName PSObject -Property #{
# We definitely want to know which computer and user profile the results are for!
ComputerName = $Computer
ProfileName = $user.Name
# And finally we want the results of `Test-Path`
Result = $t
}
Here's another option. Though nowhere near as elegant as what Matthias gave you. ;-}
It's just a refactor, to narrow down your code and pass everything directly and output by default, without the need for all the, Write-* stuff and the like. PowerShell just grants a number of ways to accomplish a use case.
Clear-Host
$null = New-Item -Path 'C:\Temp\Chrome.csv' -Force
$Status = $null
$env:COMPUTERNAME,'Localhost', '127.0.0.1' |
Foreach {
Get-ChildItem "\\$PSItem\C$\users\" |
foreach {
$ErrorActionPreference = 'silentlycontinue'
# Use variable squeezing to assign and output to the screen
($path = $PSItem.fullname)
If (test-path -Path "$path\AppData\Local\Google\Chrome\User Data\Default") {$Status = 'Has it'}
Else {$Status = 'no'}
}
[PSCustomObject] #{
ComputerName = $PSItem
Status = $Status
} | Export-Csv -Path 'C:\Temp\Chrome.csv' -Append
}
'Script finished. Please check output files'
# Results on screen
<#
\\104DB2FE-76B8-4\C$\users\ContainerAdministrator
\\104DB2FE-76B8-4\C$\users\ContainerUser
\\104DB2FE-76B8-4\C$\users\Public
\\104DB2FE-76B8-4\C$\users\WDAGUtilityAccount
\\Localhost\C$\users\ContainerAdministrator
\\Localhost\C$\users\ContainerUser
\\Localhost\C$\users\Public
\\Localhost\C$\users\WDAGUtilityAccount
\\127.0.0.1\C$\users\ContainerAdministrator
\\127.0.0.1\C$\users\ContainerUser
\\127.0.0.1\C$\users\Public
\\127.0.0.1\C$\users\WDAGUtilityAccount
Script finished. Please check output files
#>
Import-Csv -Path 'C:\Temp\Chrome.csv'
# Results
<#
104DB2FE-76B8-4 no
Localhost no
127.0.0.1 no
#>
Clear-Host
$null = New-Item -Path 'C:\Temp\Chrome.csv' -Force
$Status = $null
$env:COMPUTERNAME,'Localhost', '127.0.0.1' |
Foreach {
Get-ChildItem "\\$PSItem\C$\users\" |
foreach {
$ErrorActionPreference = 'silentlycontinue'
# Use variable squeezing to assign and output to the screen
($path = $PSItem.fullname)
If (test-path -Path "$path\AppData\Local\MicrosoftEdge") {$Status = 'Has it'}
Else {$Status = 'no'}
}
[PSCustomObject] #{
ComputerName = $PSItem
Status = $Status
} | Export-Csv -Path 'C:\Temp\Chrome.csv' -Append
}
'Script finished. Please check output files'
# Results
<#
\\104DB2FE-76B8-4\C$\users\ContainerAdministrator
\\104DB2FE-76B8-4\C$\users\ContainerUser
\\104DB2FE-76B8-4\C$\users\Public
\\104DB2FE-76B8-4\C$\users\WDAGUtilityAccount
\\Localhost\C$\users\ContainerAdministrator
\\Localhost\C$\users\ContainerUser
\\Localhost\C$\users\Public
\\Localhost\C$\users\WDAGUtilityAccount
\\127.0.0.1\C$\users\ContainerAdministrator
\\127.0.0.1\C$\users\ContainerUser
\\127.0.0.1\C$\users\Public
\\127.0.0.1\C$\users\WDAGUtilityAccount
Script finished. Please check output files
#>
Import-Csv -Path 'C:\Temp\Chrome.csv'
# Results
<#
ComputerName Status
------------ ------
104DB2FE-76B8-4 Has it
Localhost Has it
127.0.0.1 Has it
#>

How to Update the Modified By and Created By fields using PnP

I'm trying to upload a fileshare from my local machine to SharePoint using Add-PnPFile, i also have csv that has all the properties("Modified By", "Created By") for each file.
I have written this code below to grab the all the properties of the files from a csv document and tested to see if the user existed in the tenant before the using the Add-PnPFile command to upload the file.
Function Upload-Nom{
Param (
[parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName')]
[string[]]$Path = $PWD
)
Begin {}
Process {
ForEach ($item in $Path) {
#iterate all the file urls in the csv
Import-Csv $item | ForEach-Object {
#capture all the properties you need to update the file properties on sharepoint
$name = $_.Name
$fullName = $_.FullName
$itemtype = $_.'Item Type'
$relativepath = $_.Path -replace '(sites\/honours\/)'
$modifiedbyuser = $_.'Modified By'
$createdbyuser = $_.'Created By'
$modified = $_.Modified
$path = $_.Path -replace '\/','\'
$path = $path -replace '(sites\\honours\\)'
$fullurl ="C:\Users\modonny\Downloads\" +$path+"\"+ $name
#convert dates to SP format
[DateTime]$dateformats = New-Object System.DateTime;
if([DateTime]::TryParse($_.Modified, [ref]$dateformats)){
$cdob = $dateformats;
}
$modifieduser = Get-PnPUser | ? Title -eq $modifiedbyuser
$createduser = Get-PnPUser | ? Title -eq $createdbyuser
#check if user exists in tenancy
if($modifieduser){
$muserid = $modifiedbyuser.Email
}else{
$muserid = "john.doe#test.gov.uk"
}
if($createduser){
$cuserid = $createduser.Email
}else{
$createduser = Get-PnPUser | ? Email -EQ "john.doe#test.gov.uk"
$cuserid = "john.doe#test.gov.uk"
}
$object = #{}
$object.Add("Modified",$cdob)
$object.Add("Editor" ,$muserid)
$object.Add("Author" ,$cuserid)
if($fullurl | Test-Path){
if($itemtype -eq 'Folder'){
write-host "this item is a folder"
}else{
#upload files to sharepoint with the data in the $object variable
Add-PnPFile -Path $fullurl -Folder $relativepath -Values $object
}
}
}
}
}
Upload-Nom -Path "C:\Users\modonny\Documents\testing.csv"
When the code completes running all files are uploaded but the Modified By/Created By property isn't.
Sample script for your reference.
#region Variables
$Username = "user#tenant.onmicrosoft.com"
$Password = "password"
$siteURL = "https://tenant.sharepoint.com/sites/Community"
#endregion Variables
#region Credentials
[SecureString]$SecurePass = ConvertTo-SecureString $Password -AsPlainText -Force
[System.Management.Automation.PSCredential]$PSCredentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePass)
#endregion Credentials
Connect-PnPOnline -Url $siteURL -Credentials $PSCredentials
$user=Get-PnPUser | ? Email -eq "user1#tenant.onmicrosoft.com"
Add-PnPFile -Path "C:\Lee\test.docx" -Folder "MyDoc" -Values #{Editor=""+$user.Id+"";Modified="7/24/2019"}

Powershell ISE setting a path from dataset return

I'm trying to set the path that I'm going to navigate to from a field in our database. However; I keep running into errors regarding the | Format-Table command. Removing the Format-Table does not permit the dataset to be displayed so I need to keep that there. When I Write-Host $Path the correct value is returned but If I try to set-location $Path that is when I get the error:
The object of Type "Microsoft.PowerShell.Commands.Internal.Format.FormatStartData" is not valid or not in the correct sequence.
This is likely caused by a user-specified "format-table" command which is conflicting with the default formatting.
I've included the code I'm using below. Sorry if this ends up being simple I'm knew to Powershell.
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$DataSet = New-Object System.Data.DataSet
$SqlConnection.ConnectionString = "MyConnection"
cls
$SQLQuery = "SELECT setting, value FROM Table"
$SqlConnection.Open()
$SqlCmd.CommandText = $SQLQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$DataSet.Tables[0] | Format-Table -Autosize
$SqlConnection.Close()
foreach ($Row in $dataset.Tables[0].Rows)
{
IF ("$($Row.setting)" -eq "ArchiveDir" -and "$($Row.value)" -like "\\*")
{
$Path = "$($Row.value)"
$Path
set-location $Path
dir
}
ELSE {$Path = ""}
}
Not sure I fully follow your question; but give this a try:
$ds = $DataSet.Tables[0]
$ds | Format-Table -Autosize
...
foreach ($row in $ds.Rows) {
if ($row.setting -eq "ArchiveDir" -and $row.value -like "\\*") {
$path = $row.value
Write-Host $path
Set-Location $path
}
}
If you get the same error try:
$path = $row.value.ToString()
Set-Location $Path