I have a problem with this code. It prints "x86 operating system", even though the write-host, $OSArchitecture, states the architecture is 64-bit.
$OSArchitecture = (Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture -ErrorAction Stop).OSArchitecture
write-host = $OSArchitecture
if ($OSArchitecture -eq '*64*')
{
Write-Host "x64 operating system"
$Version = Get-ChildItem hklm:\software\wow6432node\microsoft\windows\currentversion\uninstall | ForEach-Object {Get-ItemProperty $_.pspath} | Where-Object {
$_.DisplayName -Eq 'Microsoft Lync 2013'} | Select-Object DisplayVersion
}
else
{
Write-Host "x86 operating system"
$Version = Get-ChildItem hklm:\software\microsoft\windows\currentversion\uninstall | ForEach-Object {Get-ItemProperty $_.pspath} | Where-Object {
$_.DisplayName -Eq 'Microsoft Lync 2013'} | Select-Object DisplayVersion
}
Update:
I get this error: Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
On x64 system [Environment]::Is64BitOperatingSystem gives $true. Therefore, you could do this:
if ([Environment]::Is64BitOperatingSystem)
{
Write-Host "x64 operating system"
$Version = Get-ChildItem hklm:\software\wow6432node\microsoft\windows\currentversion\uninstall | ForEach-Object {Get-ItemProperty $_.pspath} | Where-Object {
$_.DisplayName -Eq 'Microsoft Lync 2013'} | Select-Object DisplayVersion
}
else
{
Write-Host "x86 operating system"
$Version = Get-ChildItem hklm:\software\microsoft\windows\currentversion\uninstall | ForEach-Object {Get-ItemProperty $_.pspath} | Where-Object {
$_.DisplayName -Eq 'Microsoft Lync 2013'} | Select-Object DisplayVersion
}
Corrected code.
$OSArchitecture=Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object -ExpandProperty OSArchitecture
if ($OSArchitecture -eq "64-bit") {
Write-Output "x64 operating system"
$Path="HKLM:\software\wow6432node\microsoft\windows\currentversion\uninstall"
}
else {
Write-Output "x86 operating system"
$Path="HKLM:\software\microsoft\windows\currentversion\uninstall"
}
$Version=Get-ChildItem -Path "HKLM:\software\wow6432node\microsoft\windows\currentversion\uninstall" | ForEach-Object { Get-ItemProperty $_.pspath } | Where-Object { $_.DisplayName -eq 'Microsoft Lync 2013'} | Select-Object -ExpandProperty DisplayVersion
You can use this for this job
if ([System.IntPtr]::Size -eq 4) { "32-bit" } else { "64-bit" }
or change in your code
if ($OSArchitecture -match '64')
Related
I'm trying to build a powershell script that I can use to delete all or some of the user profiles on multiple pc's since they often cause the drives to go full.
I found the current script which I got to work for me, but I'd like to optimize it so I can input or import a list of computers where I want him to remove all the user profiles from.
Can you guys help me to input this feature?
Current Code:
$ExcludedUsers ="admin","test"
$RunOnServers = $false
[int]$MaximumProfileAge = 0 # Profiles older than this will be deleted
$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem
if ($RunOnServers -eq $true -or $osInfo.ProductType -eq 1) {
New-EventLog -LogName Application -Source "Stone Profile Cleanup" -ErrorAction SilentlyContinue
$obj = Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special -and $_.Loaded -eq $false )}
#$output = #()
foreach ($littleobj in $obj) {
if (!($ExcludedUsers -like $littleobj.LocalPath.Replace("C:\Users\",""))) {
$lastwritetime = (Get-ChildItem -Path "$($littleobj.localpath)\AppData\Local\Microsoft\Windows\UsrClass.dat" -Force ).LastWriteTime
if ($lastwritetime -lt (Get-Date).AddDays(-$MaximumProfileAge)) {
$littleobj | Remove-WmiObject
# $output += [PSCustomObject]#{
# 'RemovedSID' = $littleobj.SID
# 'LastUseTime' = $litteobj.LastUseTime
# 'LastWriteTime' = $lastwritetime
# 'LocalPath' = $littleobj.LocalPath
# }
}
}
}
#$output | Sort LocalPath | ft
#$output | Sort LocalPath | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "C:\MyOutput.TXT" -append -Encoding Unicode
Write-EventLog –LogName Application –Source "Stone Profile Cleanup" –EntryType Information –EventID 1701 -Category 2 -Message ("Profiles older than $MaximumProfileAge days have been cleaned up")
}$ExcludedUsers ="adminbholemans","testbholemans1"
$RunOnServers = $false
[int]$MaximumProfileAge = 0 # Profiles older than this will be deleted
$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem
if ($RunOnServers -eq $true -or $osInfo.ProductType -eq 1) {
New-EventLog -LogName Application -Source "Stone Profile Cleanup" -ErrorAction SilentlyContinue
$obj = Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special -and $_.Loaded -eq $false )}
#$output = #()
foreach ($littleobj in $obj) {
if (!($ExcludedUsers -like $littleobj.LocalPath.Replace("C:\Users\",""))) {
$lastwritetime = (Get-ChildItem -Path "$($littleobj.localpath)\AppData\Local\Microsoft\Windows\UsrClass.dat" -Force ).LastWriteTime
if ($lastwritetime -lt (Get-Date).AddDays(-$MaximumProfileAge)) {
$littleobj | Remove-WmiObject
# $output += [PSCustomObject]#{
# 'RemovedSID' = $littleobj.SID
# 'LastUseTime' = $litteobj.LastUseTime
# 'LastWriteTime' = $lastwritetime
# 'LocalPath' = $littleobj.LocalPath
# }
}
}
}
#$output | Sort LocalPath | ft
#$output | Sort LocalPath | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "C:\MyOutput.TXT" -append -Encoding Unicode
Write-EventLog –LogName Application –Source "Stone Profile Cleanup" –EntryType Information –EventID 1701 -Category 2 -Message ("Profiles older than $MaximumProfileAge days have been cleaned up")
}
I found this piece of code for the computer input but I'm not sure how I can implement it properly.
Get-CimInstance -ComputerName SRV1,SRV2,SRV3 -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq 'UserA' } | Remove-CimInstance
Thanks for the help everyone.
Get-CimInstance -ComputerName SRV1,SRV2,SRV3 -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('')[-1] -eq 'UserA' } | Remove-CimInstance
Do u test it before? Work OK?
I made a PS script to remove a software called Jabra.
It works fine, finding the two entries:
DisplayName UninstallString
----------- ---------------
Jabra Direct "C:\ProgramData\Package Cache\{b1b65c84-0885-49ea-bee4-b9fd0b1c5ce7}\JabraDirectSetup.exe" /uninstall
Jabra Direct MsiExec.exe /I{C5DCA8EB-FFEC-485B-84F1-924425979106}
however here's the problem: the MSI needs to be removed before the EXE or the EXE fails.
Is there a way to add a check or something for the EXE to check that the MSI has been removed before running?
Thanks,
Ste
$UninstJabra = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Get-ItemProperty | Where-Object {$_.DisplayName -eq "Jabra Direct" } | Select-Object -Property DisplayName,UninstallString
ForEach ($app in $UninstJabra){
if ($app.UninstallString -like "msiexec.exe*")
{
write-host "uninstalling $($app.DisplayName)....msi"
$app.UninstallString = $app.UninstallString -Replace "msiexec.exe","" -Replace "/I",""
$app.UninstallString = $app.UninstallString.Trim()
Start-Process msiexec.exe -ArgumentList "/X $($app.UninstallString) /qn" -wait
}
else
{
write-host "uninstalling $($app.DisplayName)....exe"
cmd /c $UninstJabra.UninstallString /silent
}
}
edit
get-package *jabra*
gives me this output
Name Version Source ProviderName
---- ------- ------ ------------
Jabra Direct 4.0.8560.0 msi
Jabra Direct 4.0.8560.0 Programs
edit 2
Sorted this way, thank you everyone
$UninstJabra = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Get-ItemProperty | Where-Object {$_.DisplayName -eq "Jabra Direct" } | Select-Object -Property DisplayName, UninstallString | Sort-Object -Property UninstallString -Descending
ForEach ($app in $UninstJabra){
if ($app.UninstallString -like "msiexec.exe*") {
write-host "uninstalling $($app.DisplayName)....msi"
$app.UninstallString = $app.UninstallString -Replace "msiexec.exe","" -Replace "/I",""
$app.UninstallString = $app.UninstallString.Trim()
Start-Process msiexec.exe -ArgumentList "/X $($app.UninstallString) /qn" -wait
}
else {
#if ($app.UninstallString -like "*ProgramData*") {
write-host "uninstalling $($app.DisplayName)....exe"
$UninstJabra = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Get-ItemProperty | Where-Object {$_.DisplayName -eq "Jabra Direct" } | Select-Object -Property DisplayName, UninstallString
cmd /c $UninstJabra.UninstallString /silent
#}
}
}
changed the order for the get-childitem to put MSI on top
To check if an application is installed then try this:
if (Get-WMIObject Win32_Product -ErrorAction SilentlyContinue | Where-Object {$_.Name -match "Jabra"}) {
# Package exists
}
Else {
# Package does not exist
}
I assume it will output both program and msi providers. You can use uninstall-package, but only with msi's. This only works in powershell 5, not above yet.
if (get-package 'jabra direct' -provider msi) { 'msi installed' }
This is probably a version issue, but I simply need to get the server name into the Format-Table PowerShell command.
$compArray = Get-Content C:\Users\Me\Documents\ServerList_All.txt
$Proc = foreach ($strComputer in $compArray) {
Get-WMIObject Win32_Service | Where-Object {
$_.Name -like 'SQL*' -or
$_.Name -like 'MSSQL*' -or
$_.Name -like 'OLAP*' -or
$_.Name -like 'MSDTS*' -or
$_.Name -like 'MSOLAP*' -or
$_.Name -like 'ReportServer*'
} | Sort-Object -Property Name | Format-Table $strComputer, Name, State
}
$Proc | Out-File C:\Users\ME\Documents\ServerStatus_All.txt
This works in PS v2:
| Sort-Object -Property Name | Format-Table Name, State
This does not, but does work in PS v3:
| Sort-Object -Property Name | Format-Table $strComputer, Name, State
Error:
Format-Table : Cannot convert System.Management.Automation.PSObject to one of the following types {System.String, System.Management.Automation.ScriptBlock}.
The only difference is the $strComputer variable. I am reading from a text file, and everything is beautiful in v3+.
No, I cannot upgrade to a newer PS version on the server I am running this from, sadly.
This should work for you in both...
$compArray = (Get-ADComputer -Filter *).Name
$Proc = foreach ($strComputer in $compArray) {
Get-WMIObject -Class Win32_Service -ComputerName $strComputer |
Where-Object {
$_.Name -like 'SQL*' -or
$_.Name -like 'MSSQL*' -or
$_.Name -like 'OLAP*' -or
$_.Name -like 'MSDTS*' -or
$_.Name -like 'MSOLAP*' -or
$_.Name -like 'ReportServer*'
} |
Select-Object -Property #{Name = 'Computer';Expression={$strComputer}}, Name, State |
Sort-Object -Property Name |
Format-Table -AutoSize
}
$Proc
# Results
Computer Name State
-------- ---- -----
LABSQL01 MSSQLFDLauncher Running
LABSQL01 MSSQLSERVER Running
LABSQL01 SQLBrowser Stopped
LABSQL01 SQLSERVERAGENT Running
LABSQL01 SQLTELEMETRY Running
LABSQL01 SQLWriter Running
Computer Name State
-------- ---- -----
LABWSM01 MSSQL$MICROSOFT##WID Stopped
I am trying to execute PowerShell script which this code:
function Invoke-InstallationOfANewBuild() {
param (
$PathToTheLocalFolderWhereBuildsAreHeld = "$($env:USERPROFILE)\Desktop\",
$PlaceOnANetworkDriveWhereBuildsAreHeld = "\\r\P\Al\OSystem\D B\20\x64"
)
begin {
Write-Verbose -Message "Searching for a build with the biggest CL number a in build name in local folder." -Verbose
$CheckClNumberOfABuildOnADesktop = Get-ChildItem $PathToTheLocalFolderWhereBuildsAreHeld -Filter *.exe | Where-Object Name -Like '*OSystemInstaller_20*' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1
Write-Verbose -Message "Searching for a build with the biggest CL number in a build name on a network drive." -Verbose
$CheckClNumberOfABuildOnANetworkDrive = Get-ChildItem $PlaceOnANetworkDriveWhereBuildsAreHeld -Filter *.exe | Where-Object Name -NotMatch '.*NoDB\.exe$' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1
Write-Verbose -Message "Comparison of two hash sums. Please, wait." -Verbose
if ($CheckClNumberOfABuildOnADesktop)
{
$GetHashOfFileWhichIsPlacedOnDesktop = Get-MyFileHash $CheckClNumberOfABuildOnADesktop -Algorithm MD5
$GetHashOfFileWhichIsPlacedOnNetworkDrive = Get-MyFileHash $CheckClNumberOfABuildOnANetworkDrive -Algorithm MD5
}
else {
Write-Verbose -Message "There are no O System 20-1 (dev branch) builds in specified local folder. Extracting hash of the newest build in the network folder..." -Verbose
$GetHashOfFileWhichIsPlacedOnNetworkDrive = Get-MyFileHash $CheckClNumberOfABuildOnANetworkDrive -Algorithm MD5
}
if ($GetHashOfFileWhichIsPlacedOnDesktop.MD5 -ne $GetHashOfFileWhichIsPlacedOnNetworkDrive.MD5)
{
Write-Verbose -Message "Hash sum of a file which is placed on the desktop and file in the network drive are different or there is no O System 20-1 build in specified local folder. The newest build will be copied from the network folder to to the local folder." -Verbose
}
else {
Write-Verbose -Message "Hash sum of a file which is placed on the desktop and a file on the network drive are the same. No need to copy anything." -Verbose
}
}
process {
if ($GetHashOfFileWhichIsPlacedOnDesktop.MD5 -eq $GetHashOfFileWhichIsPlacedOnNetworkDrive.MD5){
Write-Verbose -Message "Installation... Please, wait." -Verbose
Get-ChildItem $PathToTheLocalFolderWhereBuildsAreHeld -Filter *.exe | Where-Object Name -Like '*OSystemInstaller_20*' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1 | ForEach-Object { & $_ -s2 -sp"-SilentInstallation=standalone -UpdateMaterials=yestoall -UpgradeDBIfRequired=yes"}
}
else {
Write-Verbose -Message "The newest build doesn't exist in specified folder. Downloading, please wait." -Verbose
$SelectTheNewestBuildInFolder = Get-ChildItem $PlaceOnANetworkDriveWhereBuildsAreHeld -Filter *.exe | Where-Object Name -NotMatch '.*NoDB\.exe$' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1 | Copy-Item -Destination $PathToTheLocalFolderWhereBuildsAreHeld
}
$HashNumberOfCopiedBuild = Get-MyFileHash $SelectTheNewestBuildInFolder -Algorithm MD5
if ($HashNumberOfCopiedBuild.MD5 -eq $GetHashOfFileWhichIsPlacedOnNetworkDrive.MD5) {
Write-Verbose -Message "Hash sum of the copied file and hash sum of original file are the same. Builds are the same." -Verbose
Write-Verbose -Message "Installation... Please, wait." -Verbose
Get-ChildItem $PathToTheLocalFolderWhereBuildsAreHeld -Filter *.exe | Where-Object Name -Like '*OSystemInstaller*' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1 | ForEach-Object {& $_ -s2 -sp"-SilentInstallation=standalone -UpdateMaterials=yestoall -UpgradeDBIfRequired=yes"}
}
else {
Write-Verbose -Message "Hash sum of the copied file and hash sum of original file are different. Builds are the same." -Verbose
} # [Block moved]
}
}
Invoke-InstallationOfANewBuild
But the if statement in last else statement works twice (so that installation process is invoked twice). How can I put if statement inside else statement so that it can be invoked only once? Example
1) If value is true in the else than execute installation and stop the script
2) If value is false in the else than move to if in else and execute installation from there.
process {
if ($GetHashOfFileWhichIsPlacedOnDesktop.MD5 -ne $GetHashOfFileWhichIsPlacedOnNetworkDrive.MD5) {
Get-ChildItem $PlaceOnANetworkDriveWhereBuildsAreHeld -Filter *.exe | Where-Object Name -NotMatch '.*NoDB\.exe$' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1 | Copy-Item -Destination $PathToTheLocalFolderWhereBuildsAreHeld
}
$CheckClNumberOfABuildOnADesktop = Get-ChildItem $PathToTheLocalFolderWhereBuildsAreHeld -Filter *.exe | Where-Object Name -NotMatch '.*NoDB\.exe$' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1
$HashNumberOfTheCopiedBuild = Get-MyFileHash $CheckClNumberOfABuildOnADesktop -Algorithm MD5
if ($HashNumberOfTheCopiedBuild.MD5 -eq $GetHashOfFileWhichIsPlacedOnNetworkDrive.MD5) {
Write-Verbose -Message "Installation... Please, wait." -Verbose
Get-ChildItem $PathToTheLocalFolderWhereBuildsAreHeld -Filter *.exe | Where-Object Name -Like '*OrthoSystemInstaller*' | ForEach-Object {
New-Object psobject -Property #{
No = [int]([regex]::Match($_.Name, '(?<=CL)\d+').Value)
Name = $_.FullName
}
} | Sort-Object No -Descending | Select-Object -ExpandProperty Name -First 1 | ForEach-Object {& $_ -s2 -sp"-SilentInstallation=standalone -UpdateMaterials=yestoall -UpgradeDBIfRequired=yes"}
}
else {
Write-Verbose -Message "H" -Verbose
}
}
}
All I did - remove installation from first if and put to the second if.
I have a script containing a function Create-RebootData containing child functions such as Full and Full has a child function named Generate-RebootData where the output variable $Global:result is created.
Within Full there are multiple Where-Object two statements to filter the $Global:result into by date and time. Example below.
Is there an easier method to accomplish this instead of the multiple Where-Object statements?
The desired result are
Set-StrictMode -Version 1.0
Function Create-RebootData{
[CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="ViewOnly")]
Param(
[Parameter(ParameterSetName="ViewOnly")]
[Switch]$ViewOnly,
[Parameter(ParameterSetName="Full")]
[Switch]$Full,
)
Switch ($PSCmdlet.ParameterSetName){
"ViewOnly"
{
ViewOnly
}
"Full"
{
Full
}
}#end switch
Function Full{
Generate-RebootData
$Global:result | Where-Object {$_ -like '*fri?2:00*' -and $_.MaintenanceWindow `
-notmatch 'all.da.servers' -and $_.Server -match "^ITD"} | % {"{0}" -f $_.Server} | `
Out-File D:\Scripts\Full-Servers.txt -Append
$Global:result | Where-Object {$_ -like '*fri?2:00*' -and $_.MaintenanceWindow `
-notmatch 'all.da.servers' -and $_.Server -match "^ITD"} | `
% {"{0}" -f $_.MaintenanceWindow -replace `
"^NA.+", "$((get-date).AddDays(1).ToString('MM-dd-yy')) 01:50"} | `
Out-File D:\Scripts\Full-Times.txt -Append
}
Function Generate-RebootData{
IF(Get-Command Get-SCOMAlert -ErrorAction SilentlyContinue){}ELSE{Import-Module OperationsManager}
"Get Pend reboot servers from prod"
New-SCOMManagementGroupConnection -ComputerName Server01
$AlertData = Get-SCOMAlert -Criteria "MyString" | Select NetbiosComputerName
New-SCOMManagementGroupConnection -ComputerName Server02
$AlertData += Get-SCOMAlert -Criteria "MyString" | Select NetbiosComputerName
"Remove duplicates"
$AlertDataNoDupe = $AlertData | Sort NetbiosComputerName -Unique
"Create hash table"
$table = #{}
"Populate hash table"
$MaintenanceWindow = Import-Csv D:\Scripts\MaintenanceWindow2.csv
$MaintenanceWindow | ForEach-Object {$table[$_.Computername] = $_.'Collection Name'}
"Create final object"
$Global:result = #{}
"Begin Loop"
$Global:result = $AlertDataNoDupe | ForEach-Object { [PSCustomObject] #{
Server=$_.NetbiosComputerName
MaintenanceWindow= if($table.ContainsKey($_.NetbiosComputerName)){
$table[$_.NetbiosComputerName]
}Else { "Not Found!"}
PingCheck=IF(Test-Connection -Count 1 $_.NetbiosComputerName -Quiet -ErrorAction SilentlyContinue){"Alive"}
ELSE{"Dead"}
LastReboot=Try{$operatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $_.NetbiosComputerName -ErrorAction Stop
[Management.ManagementDateTimeConverter]::ToDateTime($operatingSystem.LastBootUpTime)}
Catch{"Access Denied!"}
} }
}
You can do it like this:
$Global:result | Where-Object {
$_ -like '*fri?2:00*' -and
$_.MaintenanceWindow -notmatch 'all.da.servers' -and
$_.Server -match '^ITD'
} | ForEach-Object {
'{0}' -f $_.Server | Out-File D:\Scripts\Full-Servers.txt -Append
'{0}' -f $_.MaintenanceWindow -replace '^NA.+',
'{0} 01:50' -f (Get-Date).AddDays(1).ToString('MM-dd-yy') | Out-File D:\Scripts\Full-Times.txt -Append
}
But I agree with Mathias' comment, this function probably should be refactored.