I am trying to figure out how to write a powershell script that will set all .swf extensions to open up on Internet Explorer. I was trying to do this with a command prompt similar to the example below. Unfornately my boss is requiring this to be done through powershell. Any help with this would be greatly appreciated since I have a txt file that will loop through about 400 computers and need to make these changes on.
CMD Way
C:\>ASSOC .swf
.swf=ShockwaveFlash.ShockwaveFlash
C:\>FTYPE ShockwaveFlash.ShockwaveFlash
ShockwaveFlash.ShockwaveFlash="C:\bin\FlashPlayer.exe" %1
What I am Trying:
Function Get-FileName{
[CmdletBinding()]
Param(
[String]$Filter = "|*.*",
[String]$InitialDirectory = "C:\")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
$file = Get-FileName -InitialDirectory $env:USERPROFILE\Desktop -Filter "Text files (*.txt)|*.txt|All files (*.*)|*.*"
ForEach ($item in (Get-Content $file)) {
$sitem = $item.Split("|")
$computer = $sitem[0].Trim()
$user = $sitem[1].Trim()
cmd /c assoc .swf=InternetExplorer.Application
### Will the above line automatically install on every pc? ###
}
Any help with trying to insert how to change the FTYPE in powershell so that $computer can cycle through would be greatly appreciated!
ASSOC and FTYPE are CMD.exe built-in commands, not executables, which means they can only be run in the context of CMD. The easiest way to run them is to invoke CMD from PowerShell.
cmd /c assoc .swf
cmd /c ftype ShockwaveFlash.ShockwaveFlash
If you need a "pure" PowerShell implementation, then you need to go to the registry. ASSOC and FTYPE merely write to the registry under theHKEY_CLASSES_ROOT hive. PowerShell does not have a default PSDrive for HKCR:, but that hive is also accessible under HKLM:\Software\Classes.
$ext = '.swf'
$HKCR = 'HKLM:\Software\Classes'
$ftype = Get-ItemProperty -Path "$HKCR\$ext" | select -expand '(default)'
$commandLine = Get-ItemProperty -Path "$HKCR\$ftype\shell\open" | select -expand '(default)'
$commandLine
To update these values, you simply use Set-ItemProperty on the same path.
Set-ItemProperty -Path "$HKCR\$ext" -Name '(default)' -Value 'ShockwaveFlash.ShockwaveFlash'
This requires you to run with Admin privileges. This also assumes that the key already exists. If not, you will have to create it with New-Item
if (-not (Test-Path "$HKCR\$ext")) {
New-Item -Path "$HKCR\$ext"
}
However, if all you want to do is set .swf files to open in iexplore.exe, then retrieving the values is unnecessary, as is modifying the FTYPE key. You need only change the extension association to InternetExplorer.Application instead of ShockwaveFlash.ShockwaveFlash. The following full scripts will do this:
In Batch file:
assoc .swf=InternetExplorer.Application
In PowerShell:
cmd /c assoc .swf=InternetExplorer.Application
In "pure" PowerShell, by modifying the registry:
$key = "HKLM:\Software\Classes\.swf"
$defaultName = '(default)'
$newValue = 'InternetExplorer.Application'
if (-not (Test-Path $key)) {
New-Item -Path $key
}
Set-Itemproperty -Path $key -Name $defaultName -Value $newValue
Note that modifying the registry doesn't take effect immediately. You need to also send a WM_SETTINGCHANGE event, or simply restart explorer.exe (eg: by logging off). You can find code to send the event here, but usually this isn't a problem for automated scripts because they force the user to re-login anyway.
Related
I'm re-writting a batch file that combined powershell and cmd commands to be strictly a powershell script, and I'm looking for a way to minimize/close the API confirmation window which pops up in the user's web browser after the application the script calls on starts.
My original batch file code looked like this using a shell object to minimize everything before moving on to the next command:
cd /d "%userprofile%\Desktop\streamblastoff\libraries\binaries\Snip"
start Snip.exe
timeout /t 1 /nobreak
powershell -command "(new-object -com shell.application).minimizeall()"
And this is what I have so far:
Push-Location -Path $PSScriptRoot\libraries\binaries\snip
Start-Process .\Snip.exe; (new-object -com shell.application).minimizeall()
Pop-Location
It doesn't work though, everything minimizes before the browser window appears ... which I guess I should have seen coming. Ideally I'd like to do it all a bit cleaner in my new script, and be able to close/minimize the specific tab once the window pops up in the users default browser, but I'm not sure if that'd be possible ...
needing to minimize a users default browser window after the first application starts
# First application.
$bar = "powershell"
# First application starts.
Start-Process -FilePath $bar
# Get Default Browser
$foo = Get-ItemPropertyValue -Path "Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice" -Name 'ProgId' |
ForEach-Object {
Get-ItemPropertyValue -Path "Registry::HKEY_CLASSES_ROOT\$_\shell\open\command" -Name '(default)'
} |
ForEach-Object {
$_ -match '^"([^"]+\.exe)"' | Out-Null
$matches[1]
} |
ForEach-Object {
Get-Item -Path $_
} |
ForEach-Object {
$_.BaseName
}
# End Get Default Browser
Function minimizeProcess
{
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
HelpMessage="Input a System.Diagnostics.Process object.")]
[System.Diagnostics.Process] $Process
)
Begin
{
# P/Invoke Definition
$signature = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
# Compile new class.
$myType = Add-Type -MemberDefinition $signature -Name MyClass -Namespace MyNamespace -PassThru
}
Process
{
# Minimize the window.
# For different display actions see:
# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
$myType::ShowWindowAsync($Process.MainWindowHandle, 7) | Out-Null
}
}
# Minimize $foo if $bar is running.
if ((Get-Process | ForEach-Object { $_.Name }) -contains $bar)
{
Get-Process -Name $foo |
ForEach-Object {
$process = Get-CimInstance Win32_Process -Filter "ProcessID = '$($_.Id)'"
$owner = Invoke-CimMethod -InputObject $process -MethodName GetOwner
Add-Member -InputObject $_ -NotePropertyName Owner -NotePropertyValue $owner.User
$_
} |
# Only minimize processes that belong to the user.
Where-Object { $_.Owner -like $env:USERNAME } |
ForEach-Object {
minimizeProcess $_
# foo minimizes.
}
}
PowerShell cmdlet to open a URL in the user's default browser. ยท GitHub
How to find the default browser via the registry on Windows 10 - Stack Overflow
PowerShell P/Invoke Walkthrough | Precision Computing
Add-Type (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs
Process.WaitForInputIdle Method (System.Diagnostics) | Microsoft Docs
Process.MainWindowHandle Property (System.Diagnostics) | Microsoft Docs
ShowWindowAsync function (winuser.h) - Win32 apps | Microsoft Docs
powershell - Get-Process and Process Owner - Stack Overflow
.net - How do I determine the owner of a process in C#? - Stack Overflow
everything minimizes before the browser window appears
The error you were making was you did not copy the sleep.
Push-Location -Path $PSScriptRoot\libraries\binaries\snip
Start-Process .\Snip.exe
# timeout /t 1 /nobreak
Start-Sleep 1
(new-object -com shell.application).minimizeall()
Pop-Location
When creating macros, you have to insert sleeps to accommodate delays in the graphical user interface.
Start-Sleep (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs
Timeout - delay in seconds - Windows CMD - SS64.com
be able to close/minimize the specific tab
SeleniumHQ Browser Automation
I am trying to create a script that bypasses UAC for my Cybersecurity class. For testing, I am having it create a text file on the desktop, but I get anerror when I try to run the following
if ((([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match 'S-1-5-32-544')) {
Set-Content C:\Users\student\Desktop\joe.txt 'joe mama'
} else {
$registryPath = 'HKCU:\Environment'
$Name = 'windir'
$Value = 'powershell -ep bypass -w h $PSCommandPath;#'
Set-ItemProperty -Path $registryPath -Name $name -Value $Value
schtasks /run /tn \Microsoft\Windows\DiskCleanup\SilentCleanup /I | Out-Null
Remove-ItemProperty -Path $registryPath -Name $name
}
Sorry for bad cropping I would copy and paste it but I am using a machine on a cyber range and I can't select text because my trackpad is broken
error
I invoke it using powershell -Command ((if((([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match 'S-1-5-32-544')) {;Set-Content C:\Users\student\Desktop\joe.txt 'joe mama';} else {;$registryPath = 'HKCU:\Environment';$Name = 'windir';$Value = 'powershell -ep bypass -w h $PSCommandPath;#';Set-ItemProperty -Path $registryPath -Name $name -Value $Value;schtasks /run /tn \Microsoft\Windows\DiskCleanup\SilentCleanup /I | Out-Null;Remove-ItemProperty -Path $registryPath -Name $name;};))
You have an unexpected token error on line:1 char:163. If you look at that line and investigate, you should be able to find your issue. Usually has to deal with syntax. You're passing in an awful lot of code into the -Command parameter. Saving it to a script and calling it might produce better results. Also, I am not sure why you're enclosing the command with double (( )). Passing it as a script block should work: Powershell -Command {SOME CODE}
If you're using Powershell 5.1, You can reference the manual here.
I have a .zip containing an installer (setup.exe and associated files).
How can I run setup.exe in a PowerShell script without extracting the zip?
Also, I need to pass command line parameters to setup.exe.
I tried
& 'C:\myzip.zip\setup.exe'
but I get an error
... not recognized as the name of a cmdlet, function, script file, or operable program.
This opens the exe:
explorer 'C:\myzip.zip\setup.exe'
but I cannot pass parameters.
What you're asking is not possible. You must extract the zip file in order to be able to run the executable. The explorer statement only works because the Windows Explorer does the extraction transparently in the background.
What you can do is write a custom function to encapsulate extraction, invocation, and cleanup.
function Invoke-Installer {
Param(
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path -LiteralPath $_})]
[string[]]$Path,
[Parameter(Manatory=$false)]
[string[]]$ArgumentList = #()
)
Begin {
Add-Type -Assembly System.IO.Compression.FileSystem
}
Process {
$Path | ForEach-Object {
$zip, $exe = $_ -split '(?<=\.zip)\\+', 2
if (-not $exe) { throw "Invalid installer path: ${_}" }
$tempdir = Join-Path $env:TEMP [IO.File]::GetFileName($zip)
[IO.Compression.ZipFile]::ExtractToDirectory($zip, $tempdir)
$installer = Join-Path $tempdir $exe
& $installer #ArgumentList
Remove-Item $tempdir -Recurse -Force
}
}
}
Invoke-Installer 'C:\myzip.zip\setup.exe' 'arg1', 'arg2', ...
Note that this requires .Net Framework v4.5 or newer.
I've got a collection of powershell scripts, some of which call others. Some of these subscripts can also be called on their own as needed. How can I quickly add logging to all of the scripts so that any script invocation results in a log file for later examination?
There are a number of questions dealing with logging with some great answers, like this one. But I wanted to see what we could come up with that:
required minimal touching of the existing powershell files
automatically dealt with script A.ps1 calling script B.ps1. If you call
A.ps1, A.ps1 needs to start and finish the logging. But if you call B.ps1
directly, B.ps1 does.
I came up with my answer below, and wanted to share and see if there were other ideas on how to approach this, or suggestions for improvement on my answer.
The support code I write (further down) allows for just adding the following to each ps1 file. It automatically gives me logging regardless of if a script is called at top-level or by another script:
#any params for script
. "$PSScriptRoot\ps_support.ps1"
StartTranscriptIfAppropriate
try
{
#all of the original script
}
finally
{
ConditionalStopTranscript
}
The code that powers this is in ps_support.ps1, sitting next to my collection of powershell files that need logging. It uses Get-Variable and Set-Variable to manipulate a couple variables at the caller's scope level:
Logging__TranscriptStarted is normal so sub-scopes can see that
logging is already happening and not try to start it again.
Logging__TranscriptStartedPrivate is private so a scope can know if
it is responsible for stopping the logging.
Here is ps_support.ps1:
Set-Variable -name TranscriptStartedPropertyName -opt ReadOnly -value 'Logging__TranscriptStarted'
Set-Variable -name TranscriptStartedPrivatePropertyName -opt ReadOnly -value 'Logging__TranscriptStartedPrivate'
function StartTranscriptIfAppropriate
{
$transcriptStarted = [bool](Get-Variable -name $TranscriptStartedPropertyName -ErrorAction Ignore)
if (-not $transcriptStarted)
{
$callstack = get-pscallstack
$fullPath = $callstack[$callstack.count-2].ScriptName
$name = Split-Path -Path $fullPath -Leaf
$directory = Split-Path -Path $fullPath
$logDirectory = [IO.Path]::GetFullPath("$directory\..\scripts_logs")
md -force $logDirectory | out-null
$logFinalPath = "$logDirectory\$(Get-Date -Format o | foreach {$_ -replace ":", "."})_$name.log"
Set-Variable -scope 1 -name $TranscriptStartedPropertyName -value $True
Set-Variable -scope 1 -option private -name $TranscriptStartedPrivatePropertyName -value $True
Start-Transcript $logFinalPath | Write-Host
}
$immediateCallerPath = Get-Variable -scope 1 -name PSCommandPath -ValueOnly
Write-Host "Starting script at $immediateCallerPath"
}
function ConditionalStopTranscript
{
$immediateCallerPath = Get-Variable -scope 1 -name PSCommandPath -ValueOnly
Write-Host "Stopping script at $immediateCallerPath"
$transcriptStartedByMe = [bool](Get-Variable -scope 1 -name $TranscriptStartedPrivatePropertyName -ErrorAction Ignore)
if ($transcriptStartedByMe)
{
Stop-Transcript | Write-Host
}
}
The variable at the top of the script defines several commands/variables for New-PSDrive, as well as connection and installation.
After this, a function is created to open a text file and extract information out of it. I know this part works because I use it in 2 other scripts.
Lastly, The script executes the commands in the first variable.
The script will show as running successfully, but checking the remote computer reveals that nothing happened.
Prior to doing any of this activity, the remote computer has a script run against it that:
enables PSRemoting (setting firewall rules and starting WinRM), and
bypasses execution policies.
After those steps, the script below is run to install a piece of software.
$eAudIT2014V2Install = {
$eAudIT2014V2password = ConvertTo-SecureString "PasswordHere" -AsPlainText -Force
$eAudIT2014V2cred = New-Object System.Management.Automation.PSCredential('domain\user', $eAudIT2014V2password)
$eAudIT2014V2drive = New-PSDrive -Name eAudIT2014V2 -PSProvider FileSystem -Root "\\Server\Share" -Credential $eAudIT2014V2cred
$eAudIT2014V2job = Start-Job {"eAudIT2014V2:\Setup.cmd"}
Wait-Job $eAudIT2014V2job
Receive-Job $eAudIT2014V2job
}
Function Get-OpenFile($initialDirectory) {
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.InitialDirectory = $initialDirectory
$OpenFileDialog.ShowDialog()
$OpenFileDialog.Filename
$OpenFileDialog.ShowHelp = $true
}
$InputFile = Get-OpenFile
if ($InputFile -eq "Cancel") {
Write-Host "Canceled By User"
exit
} else {
$Computers = #(Get-Content -Path $InputFile)
}
foreach ($computer in $computers) {
Write-Host "Installing eAudIT 2014V2 on Selected Computers"
Invoke-Command $eAudIT2014V2Install
}
I'm noticing that if I tell this script to run something basic like notepad.exe, a dllhost process starts on the machine, but notepad never does. What am I doing wrong?
The answer is pretty simple here. All of your script is for naught if you don't tell the Invoke-Command cmdlet what computer you want to execute the code on. As it is you are simply iterating a loop and invoking that command X number of times on the local machine. You need to change that second to the last line to specify the machine to execute the code on:
Invoke-Command $eAudIT2014V2Install -ComputerName $computer