cmdkey in PowerShell doesn't work when run as a logon script? - powershell

Trying is use cmdkey in a PowerShell logon script to store credentials in the credential manager. When the script is run from PowerShell ISE everything works, but when it's run as a logon script via Group Policy everything but cmdkey works. Cannot for the life of me figure out why cmdkey will work everywhere except when the script run on logon.
# Checks if CRM for Outlook is isntalled by checking the folder path
$installed = Test-Path "C:\Program Files (x86)\Microsoft Dynamics CRM"
# Checks if the CRM has already been configured using the CoreConfigured registry entry
$configured = Get-ItemProperty -Path HKCU:\software\Microsoft\MSCRMClient -Name "CoreConfigured"
# If CRM is installed and not configured, configure it, if CRM is not installed or installed and configured, exit
If ($installed -eq "True" -and $configured.CoreConfigured -ne 1) {
$message1 = New-object -ComObject Wscript.Shell
$message1.Popup("Preparing to configure Microsoft CRM for Outlook, please make sure Outlook is closed.",10,"Systems")
# Prompts user for email address and Password to configure CRM for Outlook
$c = Get-Credential -Message "To confgiure CRM, please enter your email address and password:"
# puts user credentials into Windows Credential Manager using required CRM URLs
cmdkey /generic:Microsoft_CRM_https://disco.crm.dynamics.com/ /user: $c.Username /pass: $c.Password | Out-Null
cmdkey /generic:Microsoft_CRM_https://disco.crm4.dynamics.com/ /user: $c.Username /pass: $c.Password | Out-Null
$message2 = New-Object -ComObject Wscript.Shell
$message2.Popup("Please wait, a notification will appear when the configuration is complete.",10,"Systems")
# Silenty runs the CRM configuration Wizard with custom XML file
$exe = "C:\Program Files (x86)\Microsoft Dynamics CRM\Client\ConfigWizard\Microsoft.Crm.Application.Outlook.ConfigWizard.exe"
&$exe -p /Q /i 'C:\Program Files (x86)\Microsoft Dynamics CRM\Default_Client_Config.xml' /xa /l 'c:\temp\crminstall.txt' | Out-Null
$message3 = New-Object -ComObject Wscript.Shell
$message3.Popup("Configuration complete! You may now open Outlook!",10,"Systems")
}
else {
exit
}

I imagine cmdkey is using Microsoft's Data Protection API (DPAPI) to encrypt credentials so only the current user can retrieve them. You can't use this API unless the user's session is loaded. When your script runs, it may be too early in the logon process for the security information the DPAPI needs is loaded. I'm not sure how logon scripts work, but try putting a delay in your logon script until you get a value back.
Here's the PowerShell code that encrypts with the DPAPI:
$scope = [Security.Cryptography.DataProtectionScope]::CurrentUser
$encryptedBytes = [Security.Cryptography.ProtectedData]::Protect( $plainBytes, $null, $scope )
$decryptedBytes = [Security.Cryptography.ProtectedData]::Unprotect( $encryptedBytes, $null, 0 )
Add a loop in your logn script that tries to encrypt/decrypt some random array of bytes until it succeeds.

I had the same issue: cmdkey was not working in Powershell when run as a User Logon Script.
In my case the issue was related to the user's group membership. The user was a member of the group "Power Users", but not a member of the group "Users" (or any other group).
According to this article from Microsoft, the group "Power Users" has "no default user rights", while the group "Users" has rights to "perform common tasks, such as running applications, using local and network printers".
The solution was to add my user(s) to the group "Users". This immediately solved the issue and allowed cmdkey to work in Powershell Logon Scripts.

I had this same problem with a PowerShell GPO logon script calling cmdkey.exe. The credentials populated into Credential Manager for Users, but Administrators the credentials did not show up. I found out that the credentials are saving in Credential Manager, but for the elevated user. If you run cmdkey /list in an elevated command prompt you will see the credentials there.

Related

How to have a script check for admin privileges', and rerun itself as admin if it doesn't

So I am trying to create a solution for an issue I am having in powershell.
I need to come up with a way to have my script check if it was ran with Administrative Rights. If it wasn't it needs to rerun itself with admin rights.
My situation is special from the other times this has been asked (From all the posts I have checked) as our normal user accounts doesn't have the rights, so I need to enter alternative credentials.
If this helps, Our Administrative Accounts do have an ending identifier in the name if we can filter off this. EX. "John.Doe.A" and the .A indicates this is an Admin account.
Continuing from my comment.
Your case is not unique. It gets asked a lot here and in many other spots.
'powershell SecretManagement module' auto elevate
Sample hit:
https://petri.com/powershell-secrets-management-how-to-securely-elevate-privileges-in-powershell-scripts
You store needed credentials in the 'Windows Credential Manager', and call from the as needed. MS even provides a new Secrets Module for this kind of use case. See more details via MS Docs on the topic.
Like this:
SecretManagement and SecretStore are Generally Available
https://devblogs.microsoft.com/powershell/secretmanagement-and-secretstore-are-generally-available
and this:
Microsoft.PowerShell.SecretManagement
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.secretmanagement/?view=ps-modules
Get-Secret
Get-SecretInfo
Get-SecretVault
Register-SecretVault
Remove-Secret
Set-Secret
Set-SecretInfo
Set-SecretVaultDefault
Test-SecretVault
Unregister-SecretVault
Use this.
$adminrole = ([Security.Principal.WindowsBuiltInRole] "Administrator")
$wid = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent())
If (-not $wid.IsInRole($adminrole)) {
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"
$newProcess.Arguments = $myInvocation.MyCommand.Definition
$newProcess.Verb = "runas";
[System.Diagnostics.Process]::Start($newProcess);
exit
}

powershell print to another user's default printer

Hello!
In our environment we have separate domain admins accounts for security reasons.
I have a powershell script to create an account and sync it with aad, assign o365 license etc., etc. which should be run as domain admin. But html file inside this script should be printed on non-privileged account's printer(not remotely; I use the same PC)
I have 2 accounts:
admin#mydomain.com - this is my domain admin account
user#mydomain.com - this is my account it has no privileges in the domain and i am logged to pc with this account
Script run as admin#mydomain.com i need to print file to user#mydomain.com default printer.
$print_body | Out-File "C:\temp\$name $surname.html"
$word = New-Object -comObject Word.Application
$word.documents.add("C:\temp\$name $surname.html") > $null
$word.PrintOut()
I would like "C:\temp\$name $surname.html" to be printed on my current user's (user#mydomain.com) default printer.
Now, script prints file to admin#mydomain.com default printer (which is "Save to PDF"), script prompts the location to save the file.
This is my first question so I beg your pardon if it's stupid or unclear.
First of all, I want to thank all of you, your help is much appreciated.
Unfortunately, I couldn't achieve my goal using any comment. The solution below is a workaround, but I couldn't do it better.
Here's how I achieved my goal:
Since script should be run as admin but I am logged in as user, I have created small script(Script 1), which will prompt for admin credentials, run another script which creates user(Script 2), and as soon as user created(script is finished), run another script which prints HTML file(script 3). So it's kind of script which runs scripts :)
Picture below explains logic.
Script 1 body:
do {
try{#prompt for credentials
$cred = Get-Credential -Message "Please provide your admin account details"
#start Script 2 with those credentials
$create_user = Start-Process powershell -Credential $username -ArgumentList "C:\temp\powershell\GUI.PS1" -PassThru
$correct = $true
}
catch{"Wrong Credentials"}
}until($correct -eq $true)
#wait for script 2 to finish
$create_user.WaitForExit()
#Start script 3 (Print HTML file).
Start-Process powershell -ArgumentList "C:\temp\powershell\Print_Welcome_Sheet.ps1" -NoNewWindow
Script 2 body is more than 600 string so I'll only paste part related to this question:
$print_body | Out-File "C:\temp\Powershell\new_user.html"
Script 3 body:
#if html file exists - print it
if (test-path "C:\temp\Powershell\new_user.html") {
#Create new word application insance
$word = New-Object -comObject Word.Application
#load file
$word.documents.add("C:\temp\Powershell\new_user.html") > $null
#print file to default printer
$word.PrintOut()
#wait for 3 seconds and remove html file
Start-Sleep -Seconds 3
Remove-Item "C:\temp\Powershell\new_user.html"
}
#if file doesn't exist do nothing
else {}
This approach is a workaround, but it does exactly what I wanted to be done.

Configure SharePoint 2010 UPS with PowerShell

SOLUTION FOUND: For anyone else that happens to come across this problem, have a look-see at this: http://www.harbar.net/archive/2010/10/30/avoiding-the-default-schema-issue-when-creating-the-user-profile.aspx
TL;DR When you create UPS through CA, it creates a dbo user and schema on the SQL server using the farm account, however when doing it through powershell it creates it with a schema and user named after the farm account, but still tries to manage SQL using the dbo schema, which of course fails terribly.
NOTE: I've only included the parts of my script I believe to be relevant. I can provide other parts as needed.
I'm at my wit's end on this one. Everything seems to work fine, except the UPS Synchronization service is stuck on "Starting", and I've left it over 12 hours.
It works fine when it's set up through the GUI, but I'm trying to automate every step possible. While automating I'm trying to include every option available from the GUI so that it's present if it ever needs to be changed.
Here's what I have so far:
$domain = "DOMAIN"
$fqdn = "fully.qualified.domain.name"
$admin_pass = "password"
New-SPManagedPath "personal" -WebApplication "http://portal.$($fqdn):9000/"
$upsPool = New-SPServiceApplicationPool -Name "SharePoint - UPS" -Account "$domain\spsvc"
$upsApp = New-SPProfileServiceApplication -Name "UPS" -ApplicationPool $upsPool -MySiteLocation "http://portal.$($fqdn):9000/" -MySiteManagedPath "personal" -ProfileDBName "UPS_ProfileDB" -ProfileSyncDBName "UPS_SyncDB" -SocialDBName "UPS_SocialDB" -SiteNamingConflictResolution "None"
New-SPProfileServiceApplicationProxy -ServiceApplication $upsApp -Name "UPS Proxy" -DefaultProxyGroup
$upsServ = Get-SPServiceInstance | Where-Object {$_.TypeName -eq "User Profile Service"}
Start-SPServiceInstance $upsServ.Id
$upsSync = Get-SPServiceInstance | Where-Object {$_.TypeName -eq "User Profile Synchronization Service"}
$upsApp.SetSynchronizationMachine("Portal", $upsSync.Id, "$domain\spfarm", $admin_pass)
$upsApp.Update()
Start-SPServiceInstance $upsSync.Id
I've tried running each line one at a time by just copying it directly into the shell window after defining the variables, and none of them give an error, but there has to be something the CA GUI does that I'm missing.
For anyone else that happens to come across this problem, have a look-see at this: http://www.harbar.net/archive/2010/10/30/avoiding-the-default-schema-issue-when-creating-the-user-profile.aspx
TL;DR When you create UPS through CA, it creates a dbo user and schema on the SQL server using the farm account, however when doing it through powershell it creates it with a schema and user named after the farm account, but still tries to manage SQL using the dbo schema, which of course fails terribly.
The workaround is to put my code into its own script file, and then use Start-Process to run the script as the farm account (it's a lot cleaner than the Job method described in the linked article):
$credential = Get-Credential ("$domain\spfarm", $SecureString)
Start-Process -FilePath powershell.exe -ArgumentList "-File C:\upsSync.ps1" -Credential $credential

Teamcity and msdeploy using powershell

I developed a powershell script that accepts a buncha parameters, creates an MSDeploy string, and executes it. I've tested this powershell command:
It works on my local box (installs the web app from my local box to
a remote IIS server)
It works on the TeamCity box (installs the web
app from team city's folder structure to remote iis server)
problem:
It doesn't work when I run the command from teamcity's browser version.
The error is: ERROR_USER_NOT_ADMIN
Please note, my Teamcity Build Agent is a local admin on both my teamcity server and IIS Remote server
Powreshell Source Code:
$msDeploy = 'C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe'
$sourcePackage = $args[0]
$paramFile = $args[1]
$iisAppPath = $args[2]
$servername = $args[3]
$usreName = $args[4]
$password = $args[5]
$includeAcls = $args[6]
function UpdateParamFile($paramXMLFile, $applicationPath)
{
$doc = New-Object System.Xml.XmlDocument
$doc.Load($paramXMLFile)
#IIS Application Path (this is where the code will be deployed - it has to exist in target IIS):
$appPath = $doc.SelectSingleNode("//parameters//setParameter[#name = 'IIS Web Application Name']")
$appPath.value = $applicationPath
#Connection Strings:
#KDB Connection string:
#Save
$doc.Save($paramXMLFile)
#[xml] $xmlPars = Get-Content $paramXMLFile
#$xmlPars.parameters.setParameter | Where-Object { $_.name -eq 'IIS Web Application Name' } | select value
}
UpdateParamFile $paramFile $iisAppPath
$arguments = "-source:package=$sourcePackage", "-dest:auto,computerName=`"$servername`",userName=`"$usreName`",password=`"$password`",includeAcls=`"$includeAcls`"", "-setParamFile:$paramFile", '-verb:sync', '-disableLink:AppPoolExtension', '-disableLink:CertificateExtension', '-disableLink:ContentExtension'
&$msDeploy $arguments
Teamcity call to the above script file:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -ExecutionPolicy ByPass -File C:\TeamCity\buildAgent\work\253e6183c0596123\Debug\PMRSWebsite\DeployWeb.ps1 Debug\PMRSWebsite\Web.csproj.zip "Debug\PMRSWebsite\Web.csproj.SetParameters.xml" ^^^IIS_APP_NAME^^^ ^^^ServerName^^^ ^^^userName^^^ ^^^Password^^^ false
ERROR_USER_NOT_ADMIN
Diagnosis - This happens if you try to connect to the Remote Agent Service but
have not provided appropriate administrator credentials.
Resolution - The Remote Agent Service accepts either built-in Administrator or
Domain Administrator credentials. If you have a non-domain setup and want to
use account other that built-in administrator, please do following:
1. Create a separate user group MSDepSvcUsers on remote computer.
2. Create an local account A on both local & remote computer.
3. Add A to MSDepSvcUsers on remote computer.
4. Use account A to publish, this will allow you to publish without needing to
use built-in admin account.
via

How to run PowerShell script from a computer to untrusted domain?

I have some PowerShell scripts to update data in active directory. Now I want to run these scripts from another domain joined computer, but the user that is currently logged in does not have admin rights to AD to run the scripts. How can I pass the credentials first to connect to domain as administrator and then run the script?
I know about the command get-credentials but I don't want any manual intervention.
There is batch file which runs the script and I want to put the credentials once.
I also don't want to show the password to the logged in user. Is there any possibility we can save the password in encrypted format?
Hope there is trust between the two domains
$Server = 'XXXXXXXXXX'
$username = 'Domain\XXXXXXXXXX'
$password = 'XXXXXXXXXX'
$securepassword = ConvertTo-SecureString $Password -AsPlainText -force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList ($username,$securepassword)
Get-ADComputer -Identity $Server -Credential $cred
You can change the entire script in to exe file using PowerGUI and use credentials to save it from being opened.
or
use the script by Brenton J.W. Blawat for encryption located at http://gallery.technet.microsoft.com/scriptcenter/PowerShell-Script-410ef9df
or
use the simple script mentioned in the below article
http://www.interworks.com/blogs/trhymer/2013/07/08/powershell-how-encrypt-and-store-credentials-securely-use-automation-script
Instead of using a batch file you could write a VBS wrapper and then use the script encoder to turn it into a VBE. The script encoder is technically not supported in Vista or 7 but it still works if you can find it somewhere. The other option would be to put all your code into a .Net EXE. Once it’s compiled it would hide the password from an ordinary user. Someone that knows what they are doing could still extract it so be aware of that. The same goes of an encoded VBE.