Powershell Delete Profile script - error checking not working - windows

I have this delete profile script that prompts for a username and deletes it from each of the computers listed. The delete profile and "user is logged in" parts are both working but the part that says “No profiles found on $Computer with Name $UserName” is not. I ran my script on two computers and it successfully deleted my profile on both. I recreated my profile (logged in) and stayed logged on to one and not the other. I run it again and it gives me the message "user is logged in". For the other computer it just deleted the profile on does not display the "no profile found" message. It just skips over it and displays nothing. I have changed the "if" to an "else" but, when I do that it displays multiple lines of "no profiles found" including the computer it previously deleted the profile on.
Here is the link where most of the script is derived from.
http://techibee.com/powershell/powershell-script-to-delete-windows-user-profiles-on-windows-7windows-2008-r2/1556. Looking through the comments, no one else seemed to have any issues with that part of it.
I do not have much knowledge in PowerShell and this has just been pieced together from other scripts I have found based on our needs. Our environment is Windows 7 and Server 2008 R2. Any assistance is greatly appreciated.
$UserName=Read-host "Please Enter Username: "
$ComputerName= #("computer1","computer2")
foreach($Computer in $ComputerName) {
Write-Verbose "Working on $Computer"
if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
$Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $Computer -ea 0
foreach ($profile in $profiles) {
$objSID = New-Object System.Security.Principal.SecurityIdentifier($profile.sid)
$objuser = $objsid.Translate([System.Security.Principal.NTAccount])
$profilename = $objuser.value.split("\")[1]
if($profilename -eq $UserName) {
$profilefound = $true
try {
$profile.delete()
Write-Host -ForegroundColor Green "$UserName profile deleted successfully on $Computer"
} catch {
Write-Host -ForegroundColor Yellow "Failed to delete the profile, $UserName logged on to $Computer"
}
}
}
if(!$profilefound) {
Write-Host -ForegroundColor Cyan "No profiles found on $Computer with Name $UserName"
}
} else {
write-verbose "$Computer Not reachable"
}
}

PowerShell has a number of automatic variables that you should avoid re-using.
$Profile is one of these, it contains the paths to the Profile scripts applicable to the current session.
Use any other variable name (ie. $userprofile) and you'll be fine:
foreach ($userprofile in $profiles) {
$objSID = New-Object System.Security.Principal.SecurityIdentifier($userprofile.sid)
$objuser = $objsid.Translate([System.Security.Principal.NTAccount])
$profilename = $objuser.value.split("\")[1]
if($profilename -eq $UserName) {
$profilefound = $true
try {
$userprofile.delete()
Write-Host -ForegroundColor Green "$UserName profile deleted successfully on $Computer"
} catch {
Write-Host -ForegroundColor Yellow "Failed to delete the profile, $UserName logged on to $Computer"
}
}
}

I was able to get it working by changing the "$profilefound=$false" and making it a global variable. Also the reason why it was displaying multiple lines of "profile not found when i changed it to an else statement is because of where it was placed. It was checking against every profile on the server. When it touched every profile on the computer it displayed "profile not found".
Here is the working script.
$UserName=Read-host "Please Enter Username: "
$ComputerName= #("computer1","computer2")
$profilefound = "false"
foreach($Computer in $ComputerName) {
Write-Verbose "Working on $Computer"
if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
$Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $Computer -ea 0
foreach($userprofile in $profiles){
$objSID = New-Object System.Security.Principal.SecurityIdentifier($userprofile.sid)
$objuser = $objsid.Translate([System.Security.Principal.NTAccount])
$profilename = $objuser.value.split("\")[1]
if($profilename -eq $UserName) {
$profilefound = "true"
try {
$userprofile.delete()
Write-Host -ForegroundColor Green "$UserName profile deleted successfully on $Computer"
} catch {
Write-Host -ForegroundColor Yellow "Failed to delete the profile, $UserName logged on to $Computer"
}
}
}
}
else {
write-verbose "$Computer Not reachable"
}
if ($profilefound -eq "false") {
Write-Host -ForegroundColor Cyan "No profiles found on $Computer with Name $UserName"
}
}

Related

Powershell: Find the currently logged on user and copy a unique file from a shared drive

Title says most of it. I have generated unique files for users who will be running their scripts remotely. The script is supposed to find the name of the currently logged on user and copy that unique file to C:\Users\Public. Currently however I am running into an issue where the system seems to default to my username. I have tried multiple methods sourced from here and stack overflow and cannot seem to get a good result, as everyone ends up with my unique file. I have tried the following:
$env:username
$env:userprofile
$currentuser=[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
The script looks as such:
$currentuser=[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
if ($currentuser = "Domain/username1") {
copy-item -Path "shared network location\username1file" -Destination "C:\Users\Public"
}
elseif ($currentuser = "Domain\username2") {
copy-item -Path "shared network location\username2file" -Destination "C:\Users\Public"
}
elseif ($currentuser = "domain\username3") {
copy-item -Path "shared network location\username3file" -Destination "C:\Users\Public"
}
Can anyone provide me any advice on how to fix this?
To get the currently logged on user and not the user currently running the script, you can use WMI Win32_ComputerSystem.
Also, I would recommend using switch instead of multiple elseif:
$currentuser = (Get-WmiObject -Class Win32_ComputerSystem).UserName
$file = switch ($currentuser) {
'Domain\username1' { "shared network location\username1file"; break }
'Domain\username2' { "shared network location\username2file"; break }
'Domain\username3' { "shared network location\username3file"; break }
default { $null }
}
if ($file) {
Copy-Item -Path $file -Destination "C:\Users\Public"
}
else {
Write-Host "No file to copy defined for user '$currentuser'"
}

Downloading certain files using powershell produce corrupt files

So I have a powershell script that I wrote which crawls through a particular website and downloads all of the software hosted on the site to my local machine. The website in question is nirsoft.net, and I will include the full script below. Anyway, so I have this script that downloads all of the application files hosted on the website, when I notice something odd: while most of the file downloads completed successfully, there are several files that were not downloaded successfully, resulting in a corrupt file of 4KB:
For those of you who are familiar with Nirsoft's software, the tools are very powerful, but also constantly misidentified as dangerous because of the password cracking tools, so my guess as to why this is happening is that, since powershell's If I were to guess as to why this was happening, I would guess that, due to the fact that powershell's "Invoke-webrequest cmdlet" uses Internet Explorer's engine for its core functionality, Internet Explorer is flagging the files as dangerous and refusing to download them, thus causing powershell to fail to download the file. I confirmed this by trying to manually download each of the corrupt files using internet explorer, which marked them all as malicious. However, this is where things get strange. In order to bypass this limitation, I attempted a variety of other methods to download the file within my script, like using a pure dotnet object ( (New-object System.Net.WebClient).DownloadFile("url","file") ) and even some third party command line tools (wget for windows, wget in cygwin, etc), but no matter what I tried, not a single alternative method I used was able to download a non-corrupt file. So what I want to know is if there is a way around this, and I want to know why even third party tools are affected by this. Is there some kind of rule that any scripting tool has to use Internet Explorer's engine in order to connect to the internet or something? Thanks in advance. Oh, and one last thing before I post the script. Below is the url to one of the files that I am having difficulty in downloading via powershell, which you can use to run individual tests rather than the whole script:
enter link description here
And without further ado, here is the script. Thank again:
$VerbosePreference = "Continue"
$DebugPreference = "Continue"
$present = $true
$subdomain = $null
$prods = (Invoke-WebRequest "https://www.nirsoft.net/utils/index.html").links
Foreach ($thing in $prods)
{
If ($thing.Innertext -match "([A-Za-z]|\s)+v\d{1,3}\.\d{1,3}(.)*")
{
If ($thing.href.Contains("/"))
{
}
$page = Invoke-WebRequest "https://www.nirsoft.net/utils/$($thing.href)"
If ($thing.href -like "*dot_net_tools*")
{
$prodname = $thing.innerText.Trim().Split(" ")
}
Else
{
$prodname = $thing.href.Trim().Split(".")
}
$newlinks = $page.links | Where-Object {$_.Innertext -like "*Download*" -and ($_.href.endswith("zip") -or $_.href.endswith("exe"))}
# $page.ParsedHtml.title
#$newlinks.href
Foreach ($item in $newlinks)
{
$split = $item.href.Split("/")
If ($item.href -like "*toolsdownload*")
{
Try
{
Write-host "https://www.nirsoft.net$($item.href)"
Invoke-WebRequest "https://www.nirsoft.net$($item.href)" -OutFile "$env:DOWNLOAD\test\$($split[-1])" -ErrorAction Stop
}
Catch
{
Write-Host $thing.href -ForegroundColor Red
}
}
elseif ($item.href.StartsWith("http") -and $item.href.Contains(":"))
{
Try
{
Write-host "$($item.href)"
Invoke-WebRequest $item.href -OutFile "$env:DOWNLOAD\test\$($split[-1])" -ErrorAction Stop
}
Catch
{
Write-Host "$($item.href)" -ForegroundColor Red
}
}
Elseif ($thing.href -like "*/dot_net_tools*")
{
Try
{
Invoke-WebRequest "https://www.nirsoft.net/dot_net_tools/$($item.href)" -OutFile "$env:DOWNLOAD\test\$($split[-1])" -ErrorAction Stop
}
Catch
{
Write-Host $thing.href -ForegroundColor Red
}
}
Else
{
Try
{
Write-Host "https://www.nirsoft.net/utils/$($item.href)"
Invoke-WebRequest "https://www.nirsoft.net/utils/$($item.href)" -OutFile "$env:DOWNLOAD\test\$($item.href)" -ErrorAction Stop
}
Catch
{
Write-Host $thing.href -ForegroundColor Red
}
}
If ($item.href.Contains("/"))
{
If (!(Test-Path "$env:DOWNLOAD\test\$($split[-1])"))
{
$present = $false
}
}
Else
{
If (!(Test-Path "$env:DOWNLOAD\test\$($item.href)"))
{
$present = $false
}
}
}
}
}
If ($present)
{
Write-Host "All of the files were downloaded!!!" -ForegroundColor Green
}
Else
{
Write-Host "Not all of the files downloaded. Something went wrong." -ForegroundColor Red
}
You have two separate issues.
For anything Defender flags, it doesn't matter if you save it to disk with this or that. You could simply add an exclusion for the directory in Defender.
The other issue is pointed out by Guenther, you need to provide a referrer at least on some of the downloads. With the following changes I was able to download them all.
$VerbosePreference = "Continue"
$DebugPreference = "Continue"
$present = $true
$subdomain = $null
$path = c:\temp\downloadtest\
New-Item $path -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
Add-MpPreference -ExclusionPath $path
$prods = (Invoke-WebRequest "https://www.nirsoft.net/utils/index.html").links
Foreach ($thing in $prods)
{
If ($thing.Innertext -match "([A-Za-z]|\s)+v\d{1,3}\.\d{1,3}(.)*")
{
If ($thing.href.Contains("/"))
{
}
$page = Invoke-WebRequest "https://www.nirsoft.net/utils/$($thing.href)"
If ($thing.href -like "*dot_net_tools*")
{
$prodname = $thing.innerText.Trim().Split(" ")
}
Else
{
$prodname = $thing.href.Trim().Split(".")
}
$newlinks = $page.links | Where-Object {$_.Innertext -like "*Download*" -and ($_.href.endswith("zip") -or $_.href.endswith("exe"))}
# $page.ParsedHtml.title
#$newlinks.href
Foreach ($item in $newlinks)
{
$split = $item.href.Split("/")
If ($item.href -like "*toolsdownload*")
{
Try
{
Write-host "https://www.nirsoft.net$($item.href)"
Invoke-WebRequest "https://www.nirsoft.net$($item.href)" -OutFile "$path\$($split[-1])" -ErrorAction Stop -Headers #{Referer="https://www.nirsoft.net$($item.href)"}
}
Catch
{
Write-Host $thing.href -ForegroundColor Red
}
}
elseif ($item.href.StartsWith("http") -and $item.href.Contains(":"))
{
Try
{
Write-host "$($item.href)"
Invoke-WebRequest $item.href -OutFile "$path\$($split[-1])" -ErrorAction Stop -Headers #{Referer="$($item.href)"}
}
Catch
{
Write-Host "$($item.href)" -ForegroundColor Red
}
}
Elseif ($thing.href -like "*/dot_net_tools*")
{
Try
{
Invoke-WebRequest "https://www.nirsoft.net/dot_net_tools/$($item.href)" -OutFile "$path\$($split[-1])" -ErrorAction Stop -Headers #{Referer="https://www.nirsoft.net/dot_net_tools/$($item.href)"}
}
Catch
{
Write-Host $thing.href -ForegroundColor Red
}
}
Else
{
Try
{
Write-Host "https://www.nirsoft.net/utils/$($item.href)"
Invoke-WebRequest "https://www.nirsoft.net/utils/$($item.href)" -OutFile "$path\$($item.href)" -ErrorAction Stop -Headers #{Referer="https://www.nirsoft.net/utils/$($item.href)"}
}
Catch
{
Write-Host $thing.href -ForegroundColor Red
}
}
If ($item.href.Contains("/"))
{
If (!(Test-Path "$path\$($split[-1])"))
{
$present = $false
}
}
Else
{
If (!(Test-Path "$path\$($item.href)"))
{
$present = $false
}
}
}
}
}
If ($present)
{
Write-Host "All of the files were downloaded!!!" -ForegroundColor Green
}
Else
{
Write-Host "Not all of the files downloaded. Something went wrong." -ForegroundColor Red
}
I'd also recommend you turn the download routine into a function that you can pass the relative URL portion so you don't have to repeat code several times.

appcmd.exe set config doesn't check if username or password is invalid and sets it anyways

I'm using winexe from my backend api to run commands on Windows Domain Server. I want to set IIS App Pool Identity as an Account from Active Directory. The problem is that while using this command :
%windir%\system32\inetsrv\appcmd.exe set config /section:applicationPools ^
/[name='POOLNAME'].processModel.identityType:SpecificUser ^
/[name='POOLNAME'].processModel.userName:DOMAIN\USER ^
/[name='POOLNAME'].processModel.password:PASSWORD
It runs successfully everytime even if the username and password is incorrect. Even the pool gets Started with wrong password. However setting wrong password through GUI fails.
I want to identify when the password or username is being set wrongly.
PS: I even tried using Set-ItemProperty on powershell and the result was the same.
You can't test your credentials with AppPool, but you can definitely test them.
# Service Principal credentials
$username = 'Username'
$password = 'Password' | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList $username, $password
if (Test-Credential -Credential $credential) {
Write-Verbose "Credentials for $($credential.UserName) are valid..."
# do the appcmd stuff
}
else {
Write-Warning 'Credentials are not valid or some other logic'
}
Just add Test-Credential function definition at the top of your script
function Test-Credential {
[CmdletBinding()]
Param
(
# Specifies the user account credentials to use when performing this task.
[Parameter()]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$DS = $null
$Username = $Credential.UserName
$SplitUser = $Username.Split('\')
if ($SplitUser.Count -eq 2 ) {$Username = $SplitUser[1]}
if ($SplitUser.Count -eq 1 -or $SplitUser[0] -eq $env:COMPUTERNAME ) {
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine', $env:COMPUTERNAME)
}
else {
try {
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
}
catch {
return $false
}
}
$DS.ValidateCredentials($Username, $Credential.GetNetworkCredential().Password)
}
(PS: Code is valid even though prettifier break with backslash quote syntax)
amazingly i puzzled out that you can do it like this - but it still doesn't validate
appcmd set apppool junkapp /processmodel.password:junkpassword

PowerShell Scipt for adding new Credentials

I'm trying to make script to automaticly assign credidentials based on the group that was chose. I'm getting a lot of syntax errors. Can you help?
Function Add-OSCCredential
{
$target = Read-Host "Group number"
If($target)
{
If($target -eq 1)
{[string]$result = cmdkey /add:Group1 /user:Group1 /pass:Pass1}
[ElseIf($target -eq 2)
{[string]$result = cmdkey /add:Group2 /user:Group2 /pass:Pass2}]
{
[ElseIf($target -eq 3)
{[string]$result = cmdkey /add:Group3 /user:Group3 /pass:Pass3}]
{
If($result -match "The command line parameters are incorrect")
{Write-Error "Failed to add Windows Credential to Windows vault."}
ElseIf($result -match "CMDKEY: Credential added successfully")
{Write-Host "Credential added successfully."}
}
Else
{
Write-Error "Internet(network address) or username can not be empty,please try again."
Add-OSCCredential
}
}
Add-OSCCredential
I'd suggest you use a proper editor such as vscode which will give you lots of hints concerning bad syntax.
In your case there are a lot of [] and {} parenthesis that do not make sense.
Only considering the syntax of that function, the following should 'work':
Function Add-OSCCredential {
$target = Read-Host "Group number"
If ($target) {
If ($target -eq 1) {
[string]$result = cmdkey /add:Group1 /user:Group1 /pass:Pass1
}
ElseIf ($target -eq 2) {
[string]$result = cmdkey /add:Group2 /user:Group2 /pass:Pass2
}
ElseIf ($target -eq 3) {
[string]$result = cmdkey /add:Group3 /user:Group3 /pass:Pass3
}
If ($result -match "The command line parameters are incorrect") {
Write-Error "Failed to add Windows Credential to Windows vault."
}
ElseIf ($result -match "CMDKEY: Credential added successfully") {
Write-Host "Credential added successfully."
}
}
Else {
Write-Error "Internet(network address) or username can not be empty,please try again."
Add-OSCCredential
}
}
edit:
you'd most likely want to look into a ready-to-use PowerShell Module such as CredentialManager, this way you wouldn't have to fiddle with cmdkey.exe yourself.
The returned value for Read-Host is always a string, but you treat it like an integer in your tests.
For better readability, I suggest using a switch rather that yet another set of if..elseif..else statements.
Something like this:
function Add-OSCCredential {
$target = Read-Host "Enter Group number (1-3)"
if ($target -match '1|2|3') {
switch ([int]$target) {
1 {[string]$result = cmdkey /add:Group1 /user:Group1 /pass:Pass1}
2 {[string]$result = cmdkey /add:Group2 /user:Group2 /pass:Pass2}
3 {[string]$result = cmdkey /add:Group3 /user:Group3 /pass:Pass3}
}
if($result -match "The command line parameters are incorrect") {
Write-Error "Failed to add Windows Credential to Windows vault."
}
elseif ($result -match "Credential added successfully") {
Write-Host "Credential added successfully."
}
else {
Write-Warning $result
}
}
else {
Write-Warning "Group number must be either 1, 2 or 3. Please try again."
Add-OSCCredential
}
}
Add-OSCCredential

How to implement a password change check in Powershell?

I've created a set of virtual machines (Windows Server) with a specific admin password; these VMs have been assigned to users, and may be in use. I want to know if the user changed the admin password, and do the check so the user doesn't notice. What are good solutions in powershell?
You could create a PSCredential, then attempt to get a WmiObject from the host. Something like:
$computerNames = "host1", "host2"
$pw = ConvertTo-SecureString "adminpw" -AsPlainText -Force
foreach($computerName in $computerNames)
{
$cred = New-Object System.Management.Automation.PSCredential("$computerName\Administrator", $pw)
try
{
Get-WmiObject win32_bios -ComputerName $computerName -Credential $cred
Write-Host "$computerName = Password not changed."
}
catch [System.UnauthorizedAccessException]
{
Write-Host "$computerName = Password changed."
}
}

Resources