Deleting Report Server (2014 Native Mode) Encrypted Keys & Data [PowerShell] - windows

After I clone an instance from an image, a few manual steps need to be carried out to get the report server working correctly. Among them is the deletion of all encrypted data, including symmetric key instances on the report server database.
This step requires me to RDP to the server in question, open the Reporting Services Configuration Manager and delete the encrypted data manually.
Without carrying out this step, I get the following error when I try to load up the report server interface of the new server:
The report server cannot open a connection to the report server
database. A connection to the database is required for all requests
and processing. (rsReportServerDatabaseUnavailable)
I'm trying to automate this step, so that it runs as part of a PowerShell script to remotely delete the encrypted data.
I am aware of 'rskeymgmt -d' but this prompts the user for input when run and has no force flag available to circumvent this additional input, rendering it unusable for running remotely as far as I can see:
C:\>rskeymgmt -d
All data will be lost. Are you sure you want to delete all encrypted data from
the report server database (Y/N)?

I've found a solutions to solving this problem. Calling RSKeyMgmt -d through a remote PowerShell session and piping the Y string to the call passes the parameter that RSKeyMgmt prompts the user for. This method is based on Som DT's post on backing up report server encryption keys
I've attached the full script I am using as part of my environment cloning process.
<#
.SYNOPSIS
Deletes encrypted content from a report server
.PARAMETER MachineName
The name of the machine that the report server resides on
.EXAMPLE
./Delete-EncryptedSsrsContent.ps1 -MachineName 'dev41pc123'
Deletes encrypted content from the 'dev41pc123' report server
#>
param([string]$MachineName = $(throw "MachineName parameter required, for command line usage of this script, type: 'get-help ./Delete-EncryptedSSRS.ps1 -examples'"))
trap [SystemException]{Write-Output "`n`nERROR: $_";exit 1}
Set-StrictMode -Version Latest
try
{
Write-Output "`nCreating remote session to the '$machineName' machine now..."
$session = New-PSsession -Computername $machineName
Invoke-Command -Session $Session -ScriptBlock {"Y" | RSKeyMgmt -d}
}
catch
{
Write-Output "`n`nERROR: $_"
}
finally
{
if ($Session)
{
Remove-PSSession $Session
}
}

This is a generalisation of ShaneC's solution, to support deletion of encrypted content on non default instances:
<#
.SYNOPSIS
Deletes encrypted content from a report server
.PARAMETER MachineName
The name of the machine that the report server resides on
.EXAMPLE
./Delete-EncryptedSsrsContent.ps1 -MachineName 'dev41pc123'
Deletes encrypted content from the default instance (MSSQLSERVER) of the 'dev41pc123' report server
.EXAMPLE
./Delete-EncryptedSsrsContent.ps1 -MachineName 'dev41pc123' -InstanceName 'NonDefault'
Deletes encrypted content from the specified non-default instance (e.g. NonDefault) of the 'dev41pc123' report server
#>
param(
[Parameter(Mandatory=$true)]
[string]$MachineName = $(throw "MachineName parameter required, for command line usage of this script, type: 'get-help ./Delete-EncryptedSSRS.ps1 -examples'"),
[Parameter(Mandatory=$false)]
[string]$InstanceName)
trap [SystemException]{Write-Output "`n`nERROR: $_";exit 1}
Set-StrictMode -Version Latest
try
{
Write-Output "`nCreating remote session to the '$MachineName' machine now..."
$session = New-PSsession -Computername $MachineName
if ([string]::IsNullOrEmpty($instanceName))
{
Invoke-Command -Session $Session -ScriptBlock {"Y" | RSKeyMgmt.exe -d}
}
else
{
Write-Output "`nDeleting all encrypted content from the $InstanceName instance on the $MachineName machine now...`n"
$command = """Y""| RSKeyMgmt.exe -d -i""" + $InstanceName + """"
Invoke-Command -Session $Session -ScriptBlock { Invoke-Expression $args[0] } -ArgumentList $command
Write-Output "`n"
}
}
catch
{
Write-Output "`n`nERROR: $_"
}
finally
{
if ($Session)
{
Remove-PSSession $Session
}
}

Related

Remove an entry from credential manager for all users on Windows

I am currently implementing a "remove settings" for all users in a Windows uninstaller and came over an issue I am not even sure is possible to solve.
The application stores credential entries for the current user using the CredentialManager (keymgr.dll). Let's call the target of the credential "X". On uninstall all credentials with stored with target "X" should be removed on all users. The uninstaller of course requires administrator privileges but still I find it very difficult to accomplish this.
For the current user that command is generally solved via cmdkey /delete=:X from a command prompt. As far as I know cmdkey.exe /list only helps to list entries for the current user and can't remove local entries from another user.
I have learned that the credentials are stored as OS files under the C:\Users\_user_\AppData\Local\Microsoft\Credentials folder, but I can't know which files are the entries I want to delete and removing all would be dangerous for other applications. Also I assume removing OS files will be dangerous and could have limitations (extra UAC prompt?) as well.
Runas command is the closest shot I got but because it requires the password of the user it becomes very difficult and not something I would want in the uninstaller. I also would need a way to get the username and domain for each user and iterate them.
I would prefer to use either cmd or powershell for this.
Don't want to necro an old post but I needed to do this myself so I figured I'd add this in case anyone else needs it:
cmdkey /list | ForEach-Object{if($_ -like "*Target:*" -and $_ -like "*microsoft*"){cmdkey /del:($_ -replace " ","" -replace "Target:","")}}
Powershell one liner that will remove any credentials with Microsoft in the string.
Reference:
https://gist.github.com/janikvonrotz/7819990
I ran this and it purged it locally without needing to run as admin (but I am a local admin)
The cmdkey.exe utility when run from a batch file or a PowerShell command may encounter two issues related to special characters.
1. If run from a batch file, if the credential has "(" or ")" without the double quotes, that is left and right paren, that credential will not be removed.
2. If the credential name aka targetname, has a hyphen surronded by spaces the cmdkey will not remove or create a a credential with that string " - ".
There are a few powershell modules written to try and do this, but the only one i found that handles this exceptions was on Github
https://github.com/bamcisnetworks/BAMCIS.CredentialManager
BAMCIS.CredentialManager
Using this i was able to create credentials to set up a test environment with parens or hyphens, but more importantly to remove them by gathering the users list of cached credentials using the modules command and then passing the information in the command to the remove command to remove ALL cached credentials.
One caveat. After removing the command, after some period of time two cached credentials dynamically reappear.
So to address frequent user lock out issues i am going to try and deploy this using SCCM under user context at logoff. Otherwise a system restart after removing the credentials may be needed.
Here is a prototype script that imports the module and then uses it to remove all cached credentials, As always, test, test, test and use at your own risk!
Clear-host
import-Module "$PSScriptRoot\BAMCIS.CredentialManager\BAMCIS.CredentialManager.psd1"
$L = Get-CredManCredentialList -ErrorAction SilentlyContinue
If($L -eq $null)
{
Write-host "No Cached Credentials found to remove, no action taken"
$LASTEXITCODE = 0
Write-host "The last exit code is $LASTEXITCODE"
}
Else
{
ForEach($cred in $L)
{
Write-host "`tProcessing...`n"
Write-host "$($cred.TargetName.ToString())`n"
Write-host "$($cred.Type.ToString())`n`n"
$R = Remove-CredManCredential -TargetName $($cred.TargetName.ToString()) -Type $($cred.Type.ToString()) -Force
}
$L = Get-CredManCredentialList -ErrorAction SilentlyContinue -ErrorVariable $Cred_Error
If($L -eq $null)
{
Write-host "All Cached Credentials removed, program Complete"
$LASTEXITCODE = 0
Write-host "The last exit code is $LASTEXITCODE"
}
Else
{
Write-host "WARNING: One or more Cached Credentials were not removed, program Complete"
$LASTEXITCODE = 1
}
}
Batch, elevated cmd prompt:
To find the main ones, lists any with MS.O, Micro and teams:
for /f "tokens=1-4 Delims=:=" %A in ('cmdkey /list ^| findstr Target: ^| findstr /i "MS.O Micro Teams"') do #echo %D
This deletes all the entries for teams:
for /f "tokens=1-4 Delims=:=" %A in ('cmdkey /list ^| findstr /i teams') do #cmdkey /delete:%D
If you want to include this as a script the syntax will be slightly different. Double the %%A %%D vars
Ran into similar issue, clearing old credentials for non domain joined devices. Created logon task to clear credentials. In my case was looking for particular file server, so not to clear all creds by accident. Because of syntax issue when piping string expressions into task, was easier to create reference .ps1 then reference in task. Pushed this script through Intune....
# Full path of the file
$file = 'C:\script\clearcreds.ps1'
#If the file does not exist, create it.
if (-not(Test-Path -Path $file -PathType Leaf)) {
try {
$null = New-Item -Path "C:\ProgramData\CentraStage\Packages" -Name
"clearcreds.ps1" -ItemType "file" -Value 'cmdkey /list | ForEach-Object{if($_ -
like "*Target:*" -and $_ -like "*fileserver*"){cmdkey /del:($_ -replace " ","" -replace "Target:","")}}'
Write-Host "The file [$file] has been created."
}
catch {
throw $_.Exception.Message
}
}
#######################################
#Create ScheduledTask to Run at log on.
#######################################
$schtaskName = "Clear Cached Creds "
$schtaskDescription = "Clears Cached Creds for each user at logon."
$trigger = New-ScheduledTaskTrigger -AtLogOn
$class = cimclass MSFT_TaskEventTrigger root/Microsoft/Windows/TaskScheduler
#Execute task in users context
$principal = New-ScheduledTaskPrincipal -GroupId "S-1-5-32-545" -Id "Author"
$action3 = New-ScheduledTaskAction -Execute 'Powershell.exe' "-File C:\script\clearcreds.ps1"
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$null=Register-ScheduledTask -TaskName $schtaskName -Trigger $trigger -Action $action3 -Principal $principal -Settings $settings -Description
$schtaskDescription -Force
Start-ScheduledTask -TaskName $schtaskName

How to validate deleting a DNS entry with powershell

I have a (at least what I think) tricky problem with a Server 2008 R2 domain. I wrote a cleanup script for AD computer accounts. Beside the account in AD I want to delete SCCM and DNS accounts. But with DNS I have a problem. I need to log everything as my script will run timed as a job each day.
With the normal AD module cmdlets all work great by using something like this:
Remove-ADComputer -Identity $account -confirm:$false
if($?){
Write-Log -LogContent "Delete-OldADaccount: successfully deleted account `"$($account.name)`" with LastLogondate `"$($account.LastLogondate)`", full path `"$($account.distinguishedname)`"" -LogPath $logFile
} else {
Write-Log -LogContent "Delete-OldADaccount: failed to delete account `"$($account.name)`": $($error[0].Exception.Message)" -LogPath $logFile -Level 'Warn'
}
For deleting old DNS entries I found two solutions for Server 2008 R2 (I can't use those cool new Server 2012 DNS modules for ps):
dnscmd $DNSServer /RecordDelete $ZoneName $Computer A /f
and
Get-WmiObject -ComputerName $dnsserver -Namespace 'Root\MicrosoftDNS' -class MicrosoftDNS_AType -Filter "domainname = '$computer'" | Remove-WmiObject
But both commands (dnscmd and Remove-WmiObject) always return true, even if there were no records in DNS matching my computer account's name. So I cant use a similar construct as above.
So I tried something like this:
try{
[System.Net.DNS]::GetHostEntry($computer)
Get-WmiObject -ComputerName $dns -Namespace 'Root\MicrosoftDNS' -class MicrosoftDNS_AType -Filter "domainname = '$computer'" | Remove-WmiObject -whatif
Get-WmiObject -ComputerName $dns -Namespace 'Root\MicrosoftDNS' -class MicrosoftDNS_AAAAType -Filter "domainname = '$computer'" | Remove-WmiObject -whatif
Write-Log -LogContent "Delete-OldADaccount: successfully deleted DNS entry for `"$($computer)`"" -LogPath $logFile
}
catch {
Write-Log -LogContent "Delete-OldADaccount: failed to delete DNS entry for `"$($computer)`": $($error[0].Exception.Message)" -LogPath $logFile -Level 'Warn'
}
With the static function [System.Net.DNS]::GetHostEntry($computer) I test if there is at least an ipv4 entry (as ipv6 is deactivated on my system I would get an exception if there is only an ipv6 entry. If both ipv4 and ipv6 exist it works). If there is an entry it proceeds with the Remove-WmiObject cmdlet for ipv4 and ipv6.
If there is no such entry in DNS I get an exception and directly jump into the catch-block where I log the error.
But even with this method I have no clue later if the Remove-WmiObject was successful. I would have to do a ipconfig /flushdns and re-run the command [System.Net.DNS]::GetHostEntry($computer) to see if it now fails and interpret this as "entries deleted".
Please, is there another cmdlet or way for Server 2008 R2 to delete an entry from DNS and validate if the deletion was successful? Help ;)
I can't use those cool new Server 2012 DNS modules for ps
Yes you can, as long as you have at least one machine new enough to run them. They work just fine against a 2008 R2 domain controller. This would simplify things a lot!
Otherwise, you can still use CIM/WMI calls to retrieve the value of the record like you're already doing instead of using GetHostEntry.
Example, courtesy of Jon Dechiro
if (Get-WmiObject -ComputerName $dnsserver -Namespace 'Root\MicrosoftDNS' -class MicrosoftDNS_AType -Filter "domainname = '$computer'") {
Write-Log -LogContent "Delete-OldADaccount: failed to delete DNS entry for "$($computer)": Entry still exists on $dnsserver" -LogPath $logFile -Level 'Warn'
} else {
Write-Log -LogContent "Delete-OldADaccount: successfully deleted DNS entry for "$($computer)"" -LogPath $logFile
}

Can ADSI be used to set password for windows account requiring change at first logon

I have a modified version of this PowerShell script: https://social.technet.microsoft.com/Forums/scriptcenter/en-US/355d9293-e324-4f60-8eed-18bcc6d67fc0/adsiwinntcomputeradministratoruser-with-alternate-credentials?forum=ITCG
It fails when trying to change the password for an account with a first logon requirement (which I can change the password manually using the ctrl+alt+del prompt, but will be running this often for VM testing on image). The part that matters is:
Invoke-Command -ComputerName $ComputerName -Credential $Credential -ErrorVariable e -ArgumentList $ComputerName,$NewPassword,$User -ScriptBlock {
Param($ComputerName,$NewPassword,$User)
$Account = [ADSI]"WinNT://$ComputerName/$User,user"
$Account.PwdLastSet = 0
$Account.SetInfo()
$Account.SetPassword($NewPassword)
$Account.SetInfo()
$e
}
When I run this for an account that does not require change at first logon it completes successfully:
> Change-LocalPassword -User 'TestAccount' -Credential $wincred -OldPassword $OP -NewPassword $NP -ComputerName $computerName
Info::Change-LocalPassword::Changing password from <old> to <new>
Info::Change-LocalPassword::Service WinRM is already running on Localhost
Info::Change-LocalPassword::Trusted Hosts Value is: <computer>
Info::Change-LocalPassword Invoking Command: [adsi]WinNT://<computer>/TestAccount,user
True
When running for the account requiring first logon:
Change-LocalPassword -User $Config.win_user -Credential $wincred -OldPassword $Config.winog_passwd -NewPassword $Config.win_passwd -ComputerName $computerName
Info::Change-LocalPassword::Changing password from <old> to <new>
Info::Change-LocalPassword::Service WinRM is already running on Localhost
Info::Change-LocalPassword::Trusted Hosts Value is: <computer>
Info::Change-LocalPassword Invoking Command: [adsi]WinNT://<computer>/<user>,user
[computer] Connecting to remote server <computer> failed with the following error message : Access is denied. For more information, see
the about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OpenError: (<computer>:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : AccessDenied,PSSessionStateBroken
-Message Error::Change-LocalPassword::Could not set password for <user> on <computer> [computer] Connecting to remote server <computer> failed with the following error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.
False
The local admin account is the only account on the machine, and it is not domain joined. Has anyone else encountered this and identified a resolution?
Add a password never expired userflag:
$Account = [ADSI]"WinNT://$ComputerName/$User,user"
$Account.UserFlags = 65536
$Account.PwdLastSet = 0
$Account.SetInfo()
$Account.SetPassword($NewPassword)
$Account.SetInfo()
if you want to add "user can't change password" as well, replace the above line with this one:
$Account.UserFlags = 64 + 65536

How to change specific registry setting for another user in powershell

Goal:
To edit a specific registry key setting for a specific user, and no others, in powershell.
Known:
OS: Windows 8.1 Embedded Industry Pro (Same as Win 8.1, but with some embedded features)
I can do this manually on the target machine by opening REGEDIT, selecting HKU, then click on File Menu, click on Load Hive, navigate to the user's profile directory, e.g: c:\users\MrEd and when prompted, type in 'ntuser.dat' - import HKEY_CURRENT_USER. The Hive will be loaded into HKU where you can navigate and make necessary modifications.
Summary:
I have a powershell script that returns the SID of the specific user, but when used in context of the registry hive, the hive"is not found" -- so I'm guessing I must be missing a step? How does one "Load Hive" from Powershell? Or am I missing a special, magical, goats-entrails-on-keyboard incantation somewhere?
Param(
[string]$user= $null
)
Function GetSIDfromAcctName()
{
Param(
[Parameter(mandatory=$true)]$userName
)
$myacct = Get-WmiObject Win32_UserAccount -filter "Name='$userName'"
return $myacct.sid
}
if($user)
{
$sid = GetSIDfromAcctName $user
New-PSDrive HKU Registry HKEY_USERS
$myHiveEntry = Get-Item "HKU:\${sid}"
Write-Host "Key:[$myHiveEntry]"
}
Your existing code should work for a user whose hive is already loaded (like a currently logged in user), but it makes no attempt to load the hive.
I don't know of a way to make a programmatic call to load a hive, but you can shell out to reg.exe.
This ends up being kind of janky. It seems to have issues unloading the hive if it's in use anywhere, so I've put a bunch of crap in place in this sample to try to get rid of stuff that might be holding it open, but in my tests, it can take quite a while before the reg unload command is successful, hence the whole retry portion in the finally block.
This is super unpolished, I just whipped it up on the spot.
Function GetSIDfromAcctName()
{
Param(
[Parameter(mandatory=$true)]$userName
)
$myacct = Get-WmiObject Win32_UserAccount -filter "Name='$userName'"
return $myacct.sid
}
$user = 'someuser'
$sid = GetSIDfromAcctName -userName $user
$path = Resolve-Path "$env:USERPROFILE\..\$user\NTUSER.DAT"
try {
reg load "HKU\$sid" $path
#New-PSDrive -Name HKUser -PSProvider Registry -Root "HKEY_USERS\$sid"
#Get-ChildItem HKUser:\
Get-ChildItem Registry::\HKEY_USERS\$sid
} finally {
#Remove-PSDrive -Name HKUser
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
$retryCount = 0
$retryLimit = 20
$retryTime = 1 #seconds
reg unload "HKU\$sid" #> $null
while ($LASTEXITCODE -ne 0 -and $retryCount -lt $retryLimit) {
Write-Verbose "Error unloading 'HKU\$sid', waiting and trying again." -Verbose
Start-Sleep -Seconds $retryTime
$retryCount++
reg unload "HKU\$sid"
}
}
This doesn't use a PS drive, but that code is in there too, commented out.
Note that if you don't name the hive mount point with the SID, you won't actually need the SID at all because you use the username to find the NTUSER.DAT file anyway.

Getting Stop-NlbClusterNode : A parameter cannot be found that matches parameter name 'Credential'. error while stopping node

I am using the following script to stop the node on a machine. But getting Getting Stop-NlbClusterNode : A parameter cannot be found that matches parameter name 'Credential'. error while stopping node using power-shell script and windows 2012. Also how can I pass a password in this script. Please guide.
#This script monitors stopped application pools along with websites on the current host
$RemoteHostName = "testserver"
#set hostname
#import NLB module. In PS v3 these lines should be redundant and can be removed.
import-module NetworkLoadBalancingClusters
"Networking Load Balancing Clusters Module imported"
# requests the user's credentials and assigns the credentials to an object
$Credential = Get-Credential "domain\testuser"
"Get credentials for test user done"
#uses the nlb cmdlets to check the state of the current cluster
$clusterstatus = get-nlbclusternode -nodename $RemoteHostName
[string]$status = $clusterstatus | select -expand state
"Got the status of cluster $clusterstatus"
#if the node has already been stopped dont do anything
if ($status -eq "Stopped")
{
#donothing
"Node alrerady stopped"
}
#if the node hasnt been stopped, stop the node and then send an email out
else
{
"Starting to drain stop the node"
stop-NlbClusterNode -HostName $RemoteHostName -Credential $Credential -Drain
}
start-sleep -s 30

Resources