I need to query some WMI values using PowerShell from Windows 10 devices. The script is executed in the context of a non-admin user by some software distribution tooling.
There is a local admin account, and for the current purpose (retrieving information before wiping the system) it wouldn't be a problem to put the password in the script. As automation is a hard requirement, there is no way to deal with UAC windows or the user to enter some credentials.
Is there any way to get
$sess = New-CimSession -Credential $admincred
to work without running into Access is denied, because it isn't run in an elevated context? Can I somehow self-elevate it by just having the admin credentials?
[Edit]
The comments asked to provide more concrete information:
I want to onboard many unmanaged (i.e. no software distribution tool, no domain join) Windows 10 devices to Windows Autopilot.
The devices are not at a specific site.
The device vendor can't provide the information.
The users don't have administrative privileges
The users don't know the local admin password (I do)
Exposing the local admin password is less of a problem than the missing tech knowledge of the users (the password is considered legacy)
The firewall is preventing incoming traffic (no RDP, WinRM)
Code (Source):
$devDetail = (Get-CimInstance -CimSession $session -Namespace root/cimv2/mdm/dmmap -Class MDM_DevDetail_Ext01 -Filter "InstanceID='Ext' AND ParentID='./DevDetail'")
It is too time consuming to get the information using manual remote sessions with a tool like Teamviewer. Getting the users to download a tool from the intranet and running it would be a way to go. So I created a standalone application that builds and runs a customized PowerShell script. What won't work is getting it to run in an elevated session. I always end up with Access denied.
Can I somehow self-elevate it by just having the admin credentials?
No you cannot. UAC is designed to prevent exactly what you are trying to do. Related Q&A:
elevate without prompt - verb runas start-process
UAC Getting in the Way of EXE Install Powershell
Powershell provide credentials for RunAs
There may be many workarounds, but they all will have in common that you have to go to your machines (locally or remotely) at least once, gain administrative privileges and prepare something, e. g.:
A scheduled task that runs under your local administrator account or under SYSTEM and triggers the execution of your script
Disabling UAC (temporarily) (not recommended either way)
Installing any remote management software, services or accounts (with extra run as background job privilege)
Related
I'm writing a PowerShell script that, every few months, a third-party app will automatically call to do the following among other things:
Use [System.Web.Security.Membership]::GeneratePassword() to randomly-generate a password then use that password with Export-PfxCertificate and OpenSSL's passin.
Use a static password to authenticate via COM API to a third-party system that doesn't support API keys or anything.
What's the best way to do this securely?
As far as I'm aware, there's nothing wrong with #1 but everything I've read online regarding #2 advises:
Requiring that the user supply the credentials when the script is executed but that defeats the point of automating the process.
Using PSCredential but that's not possible here.
Using an encrypted key / password file but, as far as I'm aware, there isn't really any point in that as you're effectively still storing the password alongside the script file - if the server gets compromised then they could either read the password from the script or decrypt the password in the keyfile. Unless you use a custom encryption key which isn't stored on the server but, again, that defeats the automation.
For # 2, there are lots of resources/articles covering securing credentials when using PowerShell.
Starting with Windows Credential Manager ...
Install-Module -Name "CredentialManager"
Get-Command -Module "CredentialManager"
$Target = "YourServerName"
$UserName = "Administrator"
$Secure = Read-host -AsSecureString
New-StoredCredential -Target $Target -UserName $UserName -SecurePassword $Secure -Persist LocalMachine -Type Generic
Get-StoredCredential -Target "servername" –AsCredentialObject
Remove-StoredCredential -Target "servername"
... then looking at other methods. See this Q&A for additional approaches. Passwords in powershell logging
As for...
if the server gets compromised
... if a nefarious one is this far into your system, to be able to do this, then this kicks in: Ten Immutable Laws Of Security (Version 2.0)
Have you checked out Azure Key Vault or something similar?
If you go that route, take a look at the Az module (Windows PowerShell 5.1 or PowerShell Core). The Az.KeyVault sub module has lots of functions for working with the vault.
EDIT: To address security, we don't know much about the system. However, these are the things I would look into:
Least privilege: make sure that this service account can only do what it is supposed to do.
Time of use restrictions: if the schedule that this should execute is known, configure the system to only allow the account to successfully authenticate during that time and even JIT permissions, if possible.
Location based restrictions: Only allow the account to authenticate from the location. Best result, being able to restrict to the specific server(s) from which it should be executed (could require a dedicated egress IP(s)).
Auditing: monitor and alert for changes to these settings.
If these are in place, your exposure is that the actual system is compromised and an attacker could make unintended changes within the allowed scope during the permitted window. It is a fairly low risk, at that point, which is much better than not implementing such controls.
These could be implemented potentially within the application, or through a 3rd party trusted authentication source if it could be integrated with the application.
I've created a ps1 file that runs on our UTIL server for all workstations on our domain that checks if the computer is online, skips offline computers, checks bitlocker status, formats results, and writes to a CSV file.
The script essentially uses manage-bde -cn $Computer -status C: and works great on most machines. However, there are a few machines that are confirmed on the network and online that do not reply with the status.
I ran the same command manually in powershell on the UTIL server to the affected machines and get the result "ERROR: An error occurred while connecting to the Bitlocker management interface. Check that you have administrative rights on the computer and the computer name is correct" If I connect to the computer and check status on the computer itself, it displays results no problem.
I'm logged into the UTIL server as an admin running powershell as admin. My question is, what would cause some computers to return results successfully and others to have an issue connecting to the Bitlocker management interface? Has anyone seen this before?
What process is executing your script when you're not in an interactive session? A scheduled task, a service? What security context does that process run in?
Based on some other threads I have seen on this, you should check these items:
Not running the command as an admin
Not having a compatible TPM
The TPM being disabled in the BIOS (it is on many computers)
The TPM or BitLocker services not being started.
A TPM reporting as a 1.2 TPM when in fact it is a 1.1 TPM.
I had the same issue in my net.
Solved by setting up one rule for remote client Windows firewall. Ther rule is intended to allow WMI (Windows Management Instrumentation) access to Remote Machine (see this link for further info https://social.technet.microsoft.com/Forums/lync/en-US/a2f2abb3-35f6-4c1a-beee-d09f311b4507/group-policy-to-allow-wmi-access-to-remote-machine?forum=winservergen )
Regards
Andrea
I am trying to create a powershell startup script for my domain controlled computers that will place the computer into the the specified OU. I would like for the variables to be taken on the local computer and then passed to the remote server. Once there I would like to execute the last two lines on the server.
The script below does work if it is ran on the server however as stated above I would like to be able to execute this from a client machine. How can I make this happen?
$computername = $env:ComputerName
$new_ou = "OU=TestOU,DC=Test,DC=Controller,DC=com"
Import-Module ActiveDirectory
Get-ADComputer $computername | Move-ADObject -TargetPath $new_ou
Note: Before anyone asks...my goal is to have the OU be determined by the client IP address. I understand that there are scripts that will do the discribed above but they run strictly on the server and query the DNS. I would rather have this run as a startup script on the local computer so I an better control which computers are being moved. At this point I am not interested in tackling this issue. Only the issue of how to execute the above lines on a local machine.
I assume you want to run the last 2 lines on the server because you expect that most of your domain computers won't have the RSAT tools or AD cmdlets installed.
The way to run it on a server is to have PowerShell Remoting enabled on the server and then use Invoke-Command.
That authentication is typically done with kerberos, though you could change the method, and you can supply credentials manually (though I doubt you want to be embedding credentials in the script).
You need to consider that the user making the AD changes needs permission to do so. Usually that's a domain admin, although permission could be delegated.
If you're running this as a startup script, it's running as SYSTEM. That account authenticates on the domain as the computer account (COMPUTERNAME$). This means that the computer account needs permission to move itself, which may mean it needs the ability to write objects into all possible OUs (I don't recall offhand which permissions are needed).
So you would either need to grant this ability to all computers (any computer in Domain Computers would have the ability to move any other computer to any OU), or somehow give each computer only the ability to move itself into the correct OU (which might still be too much in the way of permissions).
Another option is to make a customized session configuration on the server with a RunAs user. You could limit the users allowed to connect to the session (to Domain Computers), and limit the allowed commands so that the connecting computers can only run a limited set of functions/cmdlets. Even better, you can write your own function to do the change and only let them run that one. With the RunAs user being a privileged user in AD, the changes will work without the connecting user having the ability to make the changes directly, and without giving the connecting user the ability to use the privileged user or elevate their own permission. Remember that the connecting user in this case is the computer account.
This last method is, I think, the best/most secure way to do what you want, if you insist that it must be initiated from the client machine.
Reconsider doing this as a server-side process. Get-ADComputer can return an IPv4 address for the object, so you could use that instead of DNS. Centralizing it would make it easier to manage and troubleshoot the process.
I have set-executionpolicy unrestricted. I was able to run scripts previously. After I got an error running a powershell script, I started getting the following error:
File C:..\test.ps1 cannot be loaded because its operation is blocked by
software restriction policies, such as those created by using Group Policy.
It doesn't matter what is in the script file I am trying to run.
From what I can tell nothing else has changed. I was doing something with a remote powershell session to a remote machine, got an error. Then was unable to run scripts locally unless I run powershell.exe as administrator.
Software Restriction Policies (SRP) have nothing to do with Powershell directly.
Someone has set a restriction on what can be run and/or from where it can be run.
This isn't related to Powershell Execution Policy, Powershell Remoting, nor administrative rights/privileges.
Typically SRP is set through Group Policy and pushed out (I'm guessing you're on a domain).
You could use rsop.msc on your machine to try to determine what the settings are and maybe which policy is applying them.
If you want more information on SRP you should probably post on ServerFault.
I've recently upgraded a number of servers from 2003 to 2008R2. Since the upgrade I've started to see the following error:
[servername] Connecting to remote server failed with the following error message : The WSMan service could not launch a host process to process the given request. Make sure the WSMan provider host server and proxy are properly registered. For more information, see the about_Remote_Troubleshooting Help topic.
The error is seemingly random. The script will work and then fail. The command to create the session is in a loop (create session, remove session) and is called numerous times as part of a set of deployment scripts. When the script fails, it fails at different points.
I've checked the event log on the local workstation (win7) destination server (win2008R2) but there are no errors that I can see.
This is the lines that randomly fails:
$session = New-PSSession -ComputerName $serverName -Credential $credential
I did not see this issue on Win2003. The scripts have not changed. I'm assuming the problem is on the destination server but cannot find any errors or logs to look at. It will work once and then fail so my deployment scripts will sometimes succeed and then fail at different points.
Any guidance on tracking down this problem would be much appreciated.
You can get this error when trying to connect to localhost with an account that's not an administrator.
It used to be possible to use accounts that weren't an administrator, but a Windows Update in January 2019 disabled the functionality for security reasons. From the patch notes:
By default, PowerShell remoting only works with administrator accounts, but can be configured to work with non-administrator accounts. Starting with this release, you cannot configure PowerShell remote endpoints to work with non-administrator accounts. When attempting to use a non-administrator account, the following error will appear:“New-PSSession: [computerName] Connecting to remote server localhost failed with the following error message: The WSMan service could not launch a host process to process the given request. Make sure the WSMan provider host server and proxy are properly registered. For more information, see the about_Remote_Troubleshooting Help topic.”
You need to be setting the WSMan TrustedHosts. If you want, you can set it to everything using wildcards (*).
You can do it via PowerShell: Set-Item WSMan:\localhost\Client\TrustedHosts -Value *.
Keep in mind that you also need to enable the Windows Remote service. Use the native winrm qc command for this. Enable-PSRemoting -Force might do it as well.
You can also use the PSExec Tools from Sysinternals. Keep in mind that these tools will likely be blocked by your EndPoint Security, so don't forget to white list it.
Is there a specific reason you migrate your old OSes to a newer, but still EOL OS? You can do a lot via PowerShell in 2008R2, but it's still pretty limited. IMO, Using PowerShell is best starting from 2012R2 and onwards.
Are you hitting the number of processes limit by creating pssessions that are crashing and leaving processes open?
Default limit is 15. I'd agree with the above comment and not use sessions, instead use invoke-command like:
invoke-command -scriptblock $scriptBlock -ArgumentList $args -computername $compName -Credential $encodedRemoteCredentials
to Check your limit:
PS C:\aws> ls WSMan:\localhost\Shell
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Shell
Name Value
---- ----
MaxProcessesPerShell 15
As a quick and dirty test - next time your pssession version of your script fails, increase the maxProcessesPerShell limit using set-item cmdlet to 50 and retry. If the script no longer fails, you know that's the issue (and should consider moving to invoke-command!).