Is there any way to set a recursive environment variable in Windows? - windows

I have an apps directory in my dropbox - I'd like to be able to access all of them from the command line without having to set up loads and loads of path variables. Is there any way to set up a recursive path variable? I tried putting ** at the end - no joy.

You can't use placeholders or anything like that in the PATH environment variable. It's just a concatenation of directories, no additional features.
So either add all of the app directories to the PATHenvironment variable or think about other ways to solve the problem. For example, you could add one directory to the PATH and place batch files named like the apps there that start the apps.

Made an account for this 11 year old question.
$path = Read-Host -Prompt "Enter the exact path that needs to be recursively added to the PATH env:"
$items = gci -Path $path -Recurse -Directory -name
$nuPath = $env:Path
$r = 0
write-Host "Env started as $nuPath"
foreach ($iitem in $items){
$addpath = ($path + "\" + $iitem)
$executabledir = $addpath + '\' + "*.exe"
if(test-path $executabledir){
Write-Host $addpath
$regexAddPath = [regex]::Escape($addPath)
$arrPath = $nuPath -split ';' | Where-Object {$_ -notMatch "^$regexAddPath\\?"}
$nuPath = ($arrPath + $addPath) -join ';'
++$r
}
}
$result = ($path + ";" + $nupath) -join ';'
$temp = New-TemporaryFile
$result.ToString() > $temp
Start-Process notepad.exe -ArgumentList $temp.FullName
$title = 'WARNING'
$question = "Your new environmental variable for PATH will be in the notepad window that popped up. are you sure you want to continue?"
$choices = '&Yes', '&No'
$decision = $Host.UI.PromptForChoice($title, $question, $choices, 1)
if ($decision -eq 0 -and $r -gt 5) {
$title = 'Are you really sure?'
$question = 'This is larger than 5 entries and this can ruin your day if you mess it up. Just doublechecking everything is OK'
$choices = '&Yes', '&No'
$decision = $Host.UI.PromptForChoice($title, $question, $choices, 1)
if ($decision -eq 0) {
$env:Path > $HOME\pathbkup.txt
[Environment]::SetEnvironmentVariable("Path", $result, "Machine")
}
else {
Write-Host 'cancelled'
}
}
else {
Write-Host 'cancelled'
}
Remove-Item $temp

Related

in powershell, copy-item and test-path are both failing silently.. is it my code, or something else...?

I frequently have to copy a single file to multiple destinations, so i'm trying to write a script to make that go faster. it seems to work fine when i'm dealing with local files, but fails without any errors when running on a file that is on a mapped network drive.
at first I was using copy-item, and I couldn't make that work, so i used robocopy. that does the trick, but if the file already exists, i have an if statement using test-path which is supposed to skip to a user input that asks if you want to overwrite.. this is not working. i should say the one that checks the folder exists is working, but the one that checks for the file name always comes back true. for now, i have it just forcing an overwrite with robocopy because most of the time that's what i'll want to do anyway.
here's what i have right now.. "K:" is the mapped network drive i'm copying to, and i'm usually copying files from another mapped network drive "T:". I also should mention i have this set up to run from the context menu in windows (7) explorer, and it passes the file path to the script via %L and $args.
any advice is appreciated. (i apologize in advance, i know it's rather rough.. This is somewhat new to me.)
$Folders = #("K:\OKKHM 800" , "K:\OKKHM 1000" , "K:\OKKHM 1002" , "K:\OKKHM 1003" , "K:\OKKHM 1004", "K:\OKKHM 1250")
$source = $args[0]
$Filename = Split-Path -Path $source -Leaf
$sourcefolder= split-path -path $source -parent
$COUNTER = 0
$successful=0
$CONFIRMATION=0
foreach($Folder in $Folders){
$newpath = $folder + "\" + $filename
WRITE-HOST $NEWPATH
if(-not(test-path -path $newpath)) {
if((test-path -path $folder)) {
WRITE-HOST 'TEST 2'
robocopy $sourcefolder $folder $filename -is -it
$successful=1
}
else{
write-host 'folder does not exist'
}
}
else {
$title = 'Existing File Will Be Overwritten'
$question = 'Are you sure you want to proceed?'
$choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$decision = $Host.UI.PromptForChoice($title, $question, $choices, 1)
if ($decision -eq 0) {
Write-Host 'confirmed'
$CONFIRMATION=1
}
else {
Write-Host 'cancelled'
$CONFIRMATION=0
}
IF ($CONFIRMATION -EQ 1) {
try {
robocopy $sourcefolder $folder $filename
$successful=1
}
catch {
throw "NO GOOD"
}
}
}
$COUNTER++
}
if ($successful -eq 1) {
WRITE-HOST 'SUMMARY: ' $COUNTER ' FILES COPIED SUCCESSFULLY.'
}
Start-Sleep 5

Powershell to Audit Folder Permissions Showing Group Membership

Background: I've been using Netwrix to audit permissions to network shares for a few years now and It's only ever worked smoothly 1 time..... So I've decided to move on to just an automated powershell script. I've run into a block. When I try to parse out the group members, it doesn't like the network name in front of the group name (TBANK). Then I also need to take the next step of just showing the name instead of the whole output of get-adgroupmember. Any help would be greatly appreciated as I'm very to to scripting with powershell. Current script below:
$OutFile = "C:\users\user1\Desktop\test.csv" # Insert folder path where you want to save your file and its name
$Header = "Folder Path,IdentityReference, Members,AccessControlType,IsInherited,InheritanceFlags,PropagationFlags"
$FileExist = Test-Path $OutFile
If ($FileExist -eq $True) {Del $OutFile}
Add-Content -Value $Header -Path $OutFile
$Folder = "\\server1.tbank.com\share1"
$ACLs = get-acl $Folder | ForEach-Object { $_.Access }
Foreach ($ACL in $ACLs){
$ID = $ACL.IdentityReference
$ID = $ID -replace 'TBANK\' , ''
$ACType = $ACL.AccessControlType
$ACInher = $ACL.IsInherited
$ACInherFlags = $ACL.InheritanceFlags
$ACProp = $ACL.PropagationFlags
$Members = get-adgroupmember $ID.
$OutInfo = $Folder + "," + $ID + "," + $Members + "," + $ACType + "," + $ACInher + "," + $ACInherFlags + "," + $ACProp
Add-Content -Value $OutInfo -Path $OutFile
}
First of all, there is a way better way to output a CSV file than by trying to write each row yourself (with the risk of missing out required quotes), called Export-Csv.
To use that cmdlet, you wil need to create an array of objects which is not hard to do.
$OutFile = "C:\users\user1\Desktop\test.csv" # Insert folder path where you want to save your file and its name
$Folder = "\\server1.tbank.com\share1"
# get the Acl.Access for the folder, loop through and collect PSObjects in variable $result
$result = (Get-Acl -Path $Folder).Access | ForEach-Object {
# -replace uses regex, so you need to anchor to the beginning of
# the string with '^' and escape the backslash by doubling it
$id = $_.IdentityReference -replace '^TBANK\\' # remove the starting string "TBANK\"
# Get-ADGroupMember can return users, groups, and computers. If you only want users, do this:
# $members = (Get-ADGroupMember -Identity $id | Where-Object { $_.objectClass -eq 'user'}).name -join ', '
$members = (Get-ADGroupMember -Identity $id).name -join ', '
# output an onbject with all properties you need
[PsCustomObject]#{
'Folder Path' = $Folder
'IdentityReference' = $id
'Members' = $members
'AccessControlType' = $_.AccessControlType
'IsInherited' = $_.IsInherited
'InheritanceFlags' = $_.InheritanceFlags -join ', '
'PropagationFlags' = $_.PropagationFlags -join ', '
}
}
# output on screen
$result | Format-List
# output to CSV file
$result | Export-Csv -Path $OutFile -Force -UseCulture -NoTypeInformation
I've added a lot of inline comments to hopefully make things clear for you.
The -UseCulture switch in the Export-Csv line makes sure the field delimiter that is used matches what is set in your system as list separator. This helps when opening the csv file in Excel.
P.S> the Get-ADGroupMember cmdlet also has a switch called -Recursive. With that, it will also get the members from groups inside groups

How can I add the device names that were not scanned (offline etc) by a PowerShell script

Very very much a PowerShell newbie here I wanted a script to scan devices on the network and report on Local Admins. Found one out there and made some minor modifications to meet my needs - but I have one mod I cant work out how to do. Hoping someone out there will know a simple way to do it ?
The scrip below will read in a list of device names - scan them and output a dated report for all devices that are live and on-line. If the device is not accessible I get the following error on screen but nothing in the report.
I would like when it encounters an error that it writes to the report file - something along the lines of "$computor was not accessible!"
The code I am using is
$date = Get-Date -Format o | foreach {$_ -replace ":", "."}
ECHO "Starting scan"
$Result = #()
foreach($server in (gc .\servers.txt)){
$computer = [ADSI](”WinNT://” + $server + “,computer”)
$Group = $computer.psbase.children.find(”Administrators”)
$Filename = "c:\" + "LocalAdminAudit" + $date + ".txt"
function getAdmins
{
ECHO "SEARCHING FOR DEVICE"
$members = ($Group.psbase.invoke(”Members”) | %
{$_.GetType().InvokeMember(”Adspath”, ‘GetProperty’, $null, $_, $null)}) -
replace ('WinNT://DOMAIN/' + $server + '/'), '' -replace ('WinNT://DOMAIN/',
'DOMAIN\') -replace ('WinNT://', '')
$members}
ECHO "READY TO WRITE OUTPUT"
$Result += Write-Output "SERVER: $server"
$Result += Write-Output ' '
$Result += ( getAdmins )
$Result += Write-Output '____________________________'
$Result += Write-Output ' '
ECHO "Record written"
}
# Added date run to report
$result += Write-Output "Date Reported: $date"
$Result > $Filename
Invoke-Item $Filename
# replace "DOMAIN" with the domain name.
ECHO "Scan Complete"
And the on screen error when a machine is off line or otherwise doesn't respond is
Exception calling "Find" with "1" argument(s): "The network path was not found.
"
At \server\users\User.Name\Powershell Scripts\Get-Local-AdminsV3.ps1:1
0 char:40
+ $Group = $computer.psbase.children.find <<<< (”Administrators”)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I would like when it encounters an error that it writes to the report file - something along the lines of "$computor was not accessible!" - I am pretty sure there must be an easy way of doing this - but I cant work it out so any tips would be greatly appreciated
As Matt, mentioned in the comments. You can use a Try/Catch block inside your function to catch the error.
I also made some other changes. The most major is that I changed the function to contain all of the code necessary to get the local administrator group. Then the loop just calls the function once per computer with the computer name. This function is then reusable.
Secondly rather than output to a text file, I changed to outputting to a CSV as is a more structured format that can be used better later.
Also rather than relying on writing to the console host, I used Write-Progress to report the progress of the loop.
$Servers = Get-Content .\servers.txt
$ExportFileName = "c:\LocalAdminAudit$date.csv"
function Get-LocalAdministrator {
[cmdletbinding()]
Param(
$ComputerName
)
$Group = [ADSI]("WinNT://$computername/Administrators,group")
try {
$Group.Invoke("Members") | ForEach-Object {
$User = ($_.GetType().InvokeMember("Adspath", 'GetProperty', $null, $_, $null) -split '/')[-2,-1] -join '\'
[PSCustomObject]#{
"User" = $User
"Server" = $ComputerName
"Date" = Get-Date -Format o | ForEach-Object {$_ -replace ":", "."}
}
}
}
catch {
[PSCustomObject]#{
"User" = "Failed to Report"
"Server" = $ComputerName
"Date" = Get-Date -Format o | ForEach-Object {$_ -replace ":", "."}
}
}
}
$LocalAdmins = foreach ($Server in $Servers) {
Write-Progress -Activity "Retrieving Local Administrators" -Status "Checking $Server" -PercentComplete (([array]::indexof($Servers,$Server)/($Server.count))*100)
Get-LocalAdministrator $Server
}
$LocalAdmins | Export-CSV $ExportFileName -NoTypeInformation
Invoke-Item $ExportFileName
Lastly, be careful of smart quotes especially when cutting and pasting between Outlook and word.

Powershell locking first and last files and I cannot delete them without close powershell ISE

So I have a Powershell ISE ( tried running as administrator and w/o ) and it is created dummy .mdf file , 50 of them
Problem is that it holds onto the 1st and last , so copying or deleting them is not working ...
This is my script
param(
$amount = 50 # $(throw "Please give an amount of files to be created")
, $size = 5 # $(throw "Please give a the size of the files")
, $folder = "C:\dev\powershell\oldlocation" # $(throw "Please give an output folder wehere the files need to be created")
, $name = 'db' # $null
, $extension = '.mdf' # $null .mdf / .ldf
)
CLS
# Check for input
if(Test-Path $folder)
{
if($name -eq $null)
{
Write-Host "No filename given. Using default setting 'dummy'" -ForegroundColor Yellow
$name = 'dummy'
}
if($extension -eq $null)
{
Write-Host "No filename extension given. Using default setting '.txt'" -ForegroundColor Yellow
$extension = 'txt'
}
elseif($extension -contains '.')
{
$extension = $extension.Substring(($extension.LastIndexOf(".") + 1), ($extension.Length - 1))
}
for($i = 1; $i -le $amount; $i++)
{
$path = $folder + '\' + $name + '_' + $i + '.' + $extension
$file = [io.file]::Create($path)
$file.SetLength($size)
$file.Close
sleep 0.5
}
}
else{
Write-Host "The folder $folder doesn't exist" -ForegroundColor Red
Exit(0)
}
When () are omitted on a method, it returns the Overload Definitions. So the line where you are trying to close the file just needs ().
$file.Close()
If you see OverloadDefinitions ever returned, that's what to look for.

Checking if registry value is equal to 1 is not working correctly

I have slopped together bits of PowerShell to remote query a list of machines, stored in a .csv file, for a registry value. If the registry key's value is equal to '1', the script should then create a text file using the machine's name as the name of the text file.
Everything works great. The script runs happily without any errors. The problem is that when I go back and remotely check a targeted registry value, I find that the value isn't 1. The script is simply creating a file for every line in the .csv.
What am I doing wrong?
EDIT*** I found a problem I had a typo in the $key variable for the registry path. 7/17/2013 2:21p
$File = Import-Csv 'c:\temp\machines.csv'
foreach ($line in $file)
{
$machinename = $line.machinename
trap [Exception] {continue}
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$MachineName)
$key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon"
$regkey = ""
$regkey = $reg.opensubkey($key)
$keyValue = ""
$keyValue = $regKey.GetValue('AutoAdminLogon')
if ($keyValue = "1")
{
try
{
$textFile = New-Item -Path "c:\temp\autologin" -Name $MachineName -ItemType "File"
}
catch
{
$msg = $_
$msg
}
}
$Results = $MachineName , $keyValue
Write-host $Results
#Output Below Here:
}
In PowerShell = is an assignment operator, not a comparison operator. Change this line:
if ($keyValue = "1")
into this:
if ($keyValue -eq "1")
For more information see Get-Help about_Operators.
You're making this way too complicated, BTW. Something like this should suffice:
$keyname = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon'
Import-Csv 'C:\temp\machines.csv' | % {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",
$_.machinename)
$key = $reg.OpenSubkey($keyname)
$value = $key.GetValue('AutoAdminLogon')
if ($value -eq "1") {
$filename = Join-Path "c:\temp\autologin" $_.machinename
try {
touch $filename
$textFile = Get-Item $filename
} catch {
$_
}
}
Write-Host ($_.machinename , $value)
}

Resources