Im currently in the process of automating an install process for a custom windows application and trying to remove a couple of the com+ applications including everything below it.
Obviously this can be done manually with a right click and delete but I need something to streamline this process and I can add powershell to my automation process.
I have tried the below code but it only seems to remove the components with in the application. While this helps I would then need to remove the application in another step.
$comCatalog = New-Object -ComObject COMAdmin.COMAdminCatalog
$appColl = $comCatalog.GetCollection("Applications")
$appColl.Populate()
$app = $appColl | where {$_.Name -eq "ApplicationName"}
$compColl = $appColl.GetCollection("Components", $app.Key)
$compColl.Populate()
$index = 0
foreach($component in $compColl) {
if ($component.Name -eq "SOMECOMPONENT.NAME") {
$compColl.Remove($index)
$compColl.SaveChanges()
}
$index++
}
I would expected to be able to remove the application and have everything below also removed.
$comCatalog = New-Object -ComObject COMAdmin.COMAdminCatalog
$appColl = $comCatalog.GetCollection("Applications")
$appColl.Populate()
for ($i=0; $i -lt $appColl.Count; $i++)
{
if ($appColl.Item($i).Name -eq "YourComponentName"){ $appColl.Remove($i) $appColl.SaveChanges() }}
Related
I'm just starting to use powershell scripts.
I am trying to automate a payment process (for personal use).
But when I enter my payment information via my script the information is not saved on the page (the data acts as a placeholder)
I tried to use a "focus()" before entering the values but it doesn't work :(
(maybe a bad use ?)
I had the idea to simulate an "ENTER" key press after entering my values but I feel like I'm misusing the syntax
Here is an example of my code:
$cardnumber = "4000 1000 2000 0000"
$nameoncard = "Alban Mysteriousguy"
$expirationdate = "10/77"
$ccv = "420"
$ie = New-Object -COMObject InternetExplorer.Application
$ie.visible = $true
$ie.Navigate(‘https://secure2.ldlc.com/fr-fr/DeliveryPayment’)
#Achat CB
while ($ie.Busy -eq $true) { Start-Sleep -Seconds 1; } #wait for browser idle
#notworking
$Carednumbervar = $ie.document.getElementsByName("CardNumber")
($Carednumbervar |Select-Object -first 1).value = $cardnumber;
#working
($ie.document.getElementsByName("OwnerName") |Select-Object -first 1).value = $nameoncard;
#notworking
($ie.document.getElementsByName("ExpirationDate") |Select-Object -first 1).value = $expirationdate;
#notworking
($ie.document.getElementsByName("Cryptogram") |Select-Object -first 1).value = $ccv;
#working
$payementButton = $ie.Document.getElementsByTagName('button') | Where-Object { ($_.ClassName -ceq 'button color2 maxi') }
$CGV = $ie.Document.getElementsByTagName('label') | Where-Object { ($_.for -ceq 'terms') }
$CGV.click
#$payementButton.click()
If anyone have an idea about how can I solve this issue or just a trick that I can try, you are welcome :)
Thanks by advance
PS: To test the script you need to create an account to Ldlc.com, add something in your cart and go to the payment page ( https://secure2.ldlc.com/fr-fr/DeliveryPayment for exemple) if needed I can upload a video of the behavior.
Edit: Here I made a youtube video to show my issue : youtu.be/pTbSWU_cudY
Thank you for the detailed code. I extracted the relevant html code and performed a simple test. I found that it works correctly. You need to cancel Enable Protected Mode in IE.
Open IE -> Tools -> Internet Options -> security tab -> uncheck option Enanle Protected Mode and apply
Something like this:
We have many self-written applications which are executed in a network location. Whenever a developer wants to publish a new version of this application, I have to close all opened files on the server where the app is located, by going to:
computer management > Shared Folders > Opened Files
then choose all the files of the application - right click and close.
I want the developers to do that by themselves via PowerShell, so I wrote a Unlock-Files function. This part of the function should close the files, with the net files $id /close call:
$result = Invoke-Command -ComputerName $server -Credential $cred -ArgumentList $workpath, $server {
param($workpath,$server)
$list = New-Object System.Collections.ArrayList
$ErrorActionPreference = "SilentlyContinue"
$adsi = [adsi]"WinNT://./LanmanServer"
$resources = $adsi.psbase.Invoke("resources") | % {
[PSCustomObject] #{
ID = $_.gettype().invokeMember("Name","GetProperty",$null,$_,$null)
Path = $_.gettype().invokeMember("Path","GetProperty",$null,$_,$null)
OpenedBy = $_.gettype().invokeMember("User","GetProperty",$null,$_,$null)
LockCount = $_.gettype().invokeMember("LockCount","GetProperty",$null,$_,$null)
}
}
$resources | ? { $_.Path -like $workpath } | tee -Variable Count | % {
$id = $_.ID
net files $id /close > $null
if ($LASTEXITCODE -eq 0) { $list.add("File successfully unlocked $($_.path)") > $null }
else { $list.add("File not unlocked (maybe still open somewhere): $($_.path)") > $null }
}
if (!( ($count.count) -ge 1 )) { $list.add("No Files to unlock found in $workpath on $server") > $null }
$list
}
I expect net files /close to behave the same way as the manual way I do directly on the server described above, but it doesn't behave like this at all. It sometimes closes the file, sometimes not. But it never closes all the necessary files, so the developer can not publish his application. also, net files almost never finishes with LastExitCode 0, but I can't get my head around why.
Is there a way to force net files to really close all the files?
why would net files behave different than closing the files manually?
Is there a native PowerShell way to do this?
Is there a list of last exit codes, so I can wrap my head around this issue further?
Thank you!
So I'm trying to output a complete KB list for all computers on a server (which works on one computer) but it doesn't recognize Get-ADcomputer as a cmdlet. When checking various sources, it appears that the AD module isn't included. As I'm doing this on a work computer/server I'm hesitant to download anything or anything of that nature.
Is there any way I can achieve the following without using the AD module or someway I might be missing how to import the module (if it exists, which I don't think it does on this system)?
# 1. Define credentials
$cred = Get-Credential
# 2. Define a scriptblock
$sb = {
$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$HistoryCount = $Searcher.GetTotalHistoryCount()
$Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object -Process {
$Title = $null
if ($_.Title -match "\(KB\d{6,7}\)") {
# Split returns an array of strings
$Title = ($_.Title -split '.*\((?<KB>KB\d{6,7})\)')[1]
} else {
$Title = $_.Title
}
$Result = $null
switch ($_.ResultCode) {
0 { $Result = 'NotStarted'}
1 { $Result = 'InProgress' }
2 { $Result = 'Succeeded' }
3 { $Result = 'SucceededWithErrors' }
4 { $Result = 'Failed' }
5 { $Result = 'Aborted' }
default { $Result = $_ }
}
New-Object -TypeName PSObject -Property #{
InstalledOn = Get-Date -Date $_.Date;
Title = $Title;
Name = $_.Title;
Status = $Result
}
} | Sort-Object -Descending:$false -Property InstalledOn | Where {
$_.Title -notmatch "^Definition\sUpdate"
}
}
#Get all servers in your AD (if less than 10000)
Get-ADComputer -ResultPageSize 10000 -SearchScope Subtree -Filter {
(OperatingSystem -like "Windows*Server*")
} | ForEach-Object {
# Get the computername from the AD object
$computer = $_.Name
# Create a hash table for splatting
$HT = #{
ComputerName = $computer ;
ScriptBlock = $sb ;
Credential = $cred;
ErrorAction = "Stop";
}
# Execute the code on remote computers
try {
Invoke-Command #HT
} catch {
Write-Warning -Message "Failed to execute on $computer because $($_.Exception.Message)"
}
} | Format-Table PSComputerName,Title,Status,InstalledOn,Name -AutoSize
You've got 3 options:
First is to just install the RSAT feature for AD which will include the AD module. This is probably the best option unless there is something specific preventing it. If you're running your script from a client operating systems you need to install the RSAT first, though.
Option 2 (which should only be used if adding the Windows feature is somehow an issue) is to download and use the Quest AD tools, which give very similar functionality, but it looks like Dell is doing their best to hide these now so that may be difficult to locate...
Option 3 is to use the .NET ADSI classes to access AD directly, which will work without any additional downloads on any system capable of running PowerShell. If you'd like to go this route you should check out the documentation for the interface Here and for the System.DirectoryServices namespace Here.
Edit
Just noticed the last part of your question, what do you mean by "a complete KB list"? Not just Windows updates or things updated manually or whatever? What else would be in a list of Windows updates that was not a Windows update?
You have not mentioned the OSes you are using but in general if you have a server 2008 R2 or above, all you have to do it activate the RSAT feature AD PowerShell Module and you will have the cmdlet you are looking for.
On a client machine, you 'have to' install RSAT, and then activate the features. You can take a look at the technet article for more info: https://technet.microsoft.com/en-us/library/ee449483(v=ws.10).aspx
If you don't want to use that option, then you will have to use .NET ADSI classes. There are tons of examples on how to do this, it basically boils down to a couple of lines really. Technet has examples on this as well: https://technet.microsoft.com/en-us/library/ff730967.aspx
I'm working on a TFS build with a pre-build PowerShell script that (in addition to building my app) automatically checks out a file where we maintain version, increments the build number, then checks the file in.
I have been able to do this, except that I get an error from the script which results in a partially successful build (orange). I need to have a fully successful (green) build.
Here's the check-in line (using TFS Power Tools for VS 2013):
New-TfsChangeset -Item $versionFile -Override "Automated" -Notes "Code Reviewer=tfs" -Comment "Automated"
The error I receive is that the changeset is not associated with a work item, but the -Override should handle that. The funny thing is that it checks in anyway.
Running locally on my machine instead of the build server, I get the same thing, except that I also see a line that says The policies have been overridden. This tells me that the override is working, but it still outputs the error.
I've tried adding -ErrorAction SilentlyContinue, but it has no effect.
I need one of three options:
A way to suppress output of the checkin error,
A way to create a work item and associate it to the checkin, or
Some other third option that will result in a green build.
Any ideas?
Credit goes to Eddie - MSFT for leading me the right direction, but I wanted to consolidate everything here.
WARNING This will check in all pending changes in the workspace.
Creating a new work item (source)
I did modify it quite a bit to support automation. It connects to TFS and generates a new work item.
function New-WorkItem()
{
# These *should* be registered in the GAC.
# The version numbers will likely have to change as new versions of Visual Studio are installed on the server.
Add-Type -Assembly "Microsoft.TeamFoundation.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Add-Type -Assembly "Microsoft.TeamFoundation.WorkItemTracking.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
$server = "http://YOURTFSSERVER:8080/tfs"
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($server)
$type = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
$store = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore] $tfs.GetService($type)
$workItem = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($store.Projects[0].WorkItemTypes[0])
$workItem.Title = "Automated work item"
$workItem
}
Associating the work item and checking in
Slight modifications to the code from the link given by Eddie, we get the following:
function CheckIn()
{
param([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem] $workItem)
$col = Get-TfsCollection("http://YOURTFSSERVER:8080/tfs/YOURCOLLECTION")
$vcs = Get-TfsVersionControlServer($col)
$ws = $vcs.GetWorkspace([System.IO.Path]::GetDirectoryName($anyPathInWorkspace))
$pc = $ws.GetPendingChanges()
$wici = Get-TfsWorkItemCheckinInfo($workItem)
$changeset = $ws.CheckIn($pc, "Automated check in", $null, $wici, $null)
}
That post doesn't tell you that Get-TfsCollection, Get-TfsVersionControlServer, and Get-TfsWorkItemCheckinInfo aren't defined. I had to find them.
I found the first two on http://nkdagility.com/powershell-tfs-2013-api-1-get-tfscollection-and-tfs-services/. I didn't have to change anything.
function Get-TfsCollection
{
param([string] $CollectionUrl)
if ($CollectionUrl -ne "")
{
#if collection is passed then use it and select all projects
$tfs = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($CollectionUrl)
}
else
{
#if no collection specified, open project picker to select it via gui
$picker = New-Object Microsoft.TeamFoundation.Client.TeamProjectPicker([Microsoft.TeamFoundation.Client.TeamProjectPickerMode]::NoProject, $false)
$dialogResult = $picker.ShowDialog()
if ($dialogResult -ne "OK")
{
#exit
}
$tfs = $picker.SelectedTeamProjectCollection
}
$tfs
}
function Get-TfsVersionControlServer
{
param([Microsoft.TeamFoundation.Client.TfsTeamProjectCollection] $TfsCollection)
$TfsCollection.GetService("Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer")
}
But I couldn't find Get-TfsWorkItemCheckinInfo. The only Google hit was the kinook link from Eddie (and soon probably this answer). Here's what I came up with:
function Get-TfsWorkItemCheckinInfo
{
param([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem] $workItem)
$wi = New-Object Microsoft.TeamFoundation.VersionControl.Client.WorkItemCheckinInfo($workItem, [Microsoft.TeamFoundation.VersionControl.Client.WorkItemCheckinAction]::Resolve)
$wi
}
Now we can use it
CheckIn (New-WorkItem)
That's it!
You can create a work item from PowerShell by following this article: http://halanstevens.com/blog/powershell-script-to-create-a-workitem/
Quote the code here for reference:
$key = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\VisualStudio\8.0
$dir = [string] (Get-ItemProperty $key.InstallDir)
$dir += "PrivateAssemblies\"
$lib = $dir + "Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
[Reflection.Assembly]::LoadFrom($lib)
$lib = $dir + "Microsoft.TeamFoundation.Client.dll"
[Reflection.Assembly]::LoadFrom($lib)
"Please enter your Team Foundation Server Name:"
$server = [Console]::ReadLine()
$server = $server.Trim()
"Connecting to " + $server + "..."
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($server)
$type = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
$store = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore] $tfs.GetService($type)
$workItem = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($store.Projects[0].WorkItemTypes[0])
"Created a new work item of type " + $workItem.Type.Name
$workItem.Title = "Created by Windows PowerShell!"
$workItem.Save()
And then refer to this article to associate the work item to changeset: http://www.kinook.com/Forum/showthread.php?t=4502
I can pin some programs to taskbar on Win7 using PowerShell.
$shell = new-object -com "Shell.Application"
$folder = $shell.Namespace('C:\Windows')
$item = $folder.Parsename('notepad.exe')
$verb = $item.Verbs() | ? {$_.Name -eq 'Pin to Tas&kbar'}
if ($verb) {$verb.DoIt()}
How do I modify the above code to pin a program to the Start menu?
Another way
$sa = new-object -c shell.application
$pn = $sa.namespace($env:windir).parsename('notepad.exe')
$pn.invokeverb('startpin')
Or unpin
$pn.invokeverb('startunpin')
Use the code below
$shell = new-object -com "Shell.Application"
$folder = $shell.Namespace('C:\Windows')
$item = $folder.Parsename('notepad.exe')
$verb = $item.Verbs() | ? {$_.Name -eq 'Pin to Start Men&u'}
if ($verb) {$verb.DoIt()}
Note: the change is in the fourth line.
The main problem with most of the solution is that they enumerate the verbs on a file, search for the string to perform the action (“Pin to Startmenu” etc.) and then execute it. This does not work if you need to support 30+ languages in your company, except you use external function to search for the localized command (see answer from shtako-verflow).
The answer from Steven Penny is the first that is language neutral and does not need any external code. It uses the verbs stored in the registry HKEY_CLASSES_ROOT\CLSID\{90AA3A4E-1CBA-4233-B8BB-535773D48449} and HKEY_CLASSES_ROOT\CLSID\{a2a9545d-a0c2-42b4-9708-a0b2badd77c8}
Based on this, here’s the code we are now using:
function PinToTaskbar {
param([Parameter(Mandatory=$true)][string]$FilePath)
ExecuteVerb $FilePath "taskbarpin"
}
function UnpinFromTaskbar {
param([Parameter(Mandatory=$true)][string]$FilePath)
ExecuteVerb $FilePath "taskbarunpin"
}
function PinToStartmenu {
param([Parameter(Mandatory=$true)][string]$FilePath)
ExecuteVerb $FilePath "startpin"
}
function UnpinFromStartmenu {
param([Parameter(Mandatory=$true)][string]$FilePath)
ExecuteVerb $FilePath "startunpin"
}
function ExecuteVerb {
param(
[Parameter(Mandatory=$true)][string]$File,
[Parameter(Mandatory=$true)][string]$Verb
)
$path = [System.Environment]::ExpandEnvironmentVariables($File)
$basePath = split-path $path -parent #retrieve only the path File=C:\Windows\notepad.exe -> C:\Windows
$targetFile = split-path $path -leaf #retrieve only the file File=C:\Windows\notepad.exe -> notepad.exe
$shell = new-object -com "Shell.Application"
$folder = $shell.Namespace($basePath)
if ($folder)
{
$item = $folder.Parsename($targetFile)
if ($item)
{
$item.invokeverb($Verb)
# "This method does not return a value." (http://msdn.microsoft.com/en-us/library/windows/desktop/bb787816%28v=vs.85%29.aspx)
# Therefore we have no chance to know if this was successful...
write-host "Method [$Verb] executed for [$path]"
}
else
{
write-host "Target file [$targetFile] not found, aborting"
}
}
else
{
write-host "Folder [$basePath] not found, aborting"
}
}
#PinToTaskbar "%WINDIR%\notepad.exe"
#UnpinFromTaskbar "%WINDIR%\notepad.exe"
PinToStartmenu "%WINDIR%\notepad.exe"
#UnpinFromStartmenu "%WINDIR%\notepad.exe"
See the script (international) here : http://gallery.technet.microsoft.com/scriptcenter/b66434f1-4b3f-4a94-8dc3-e406eb30b750
If you want to add an action like Pin to Modern UI interface (Windows 8), at $verbs, add 51201
Steven Penny's second answer above worked well for me. Here are a couple more tidbits.
It's doing COM through PowerShell, so you can do the same thing with pretty much any COM client. For example, here's an AutoHotkey version.
Shell := ComObjCreate("Shell.Application")
Target := Shell.Namespace(EnvGet("WinDir")).ParseName("Notepad.exe")
Target.InvokeVerb("startpin")
VBScript or InnoSetup would look almost the same except for the function used to create the object.
I also found that I have one program that pinned OK, but didn't have the right icon and/or description because of limitations in the compiler. I just made a little 1-line WinForms app that starts the target with Process.Start, and then added the appropriate icon, and the name I wanted in the Start Menu in the Title property in AppInfo.cs.