How can I programatically set the owner of a MSMQ queue? - powershell

I have a powershell script that creates some private queues for me. However, the queues default to being owned by whoever ran the install script. I'd prefer to have them owned by a group (i.e. - Administrators or some such). Anybody know how to do this?
foreach($strQueue in $strQueues) {
if (![System.Messaging.MessageQueue]::Exists($strQueue)) {
$q = [System.Messaging.MessageQueue]::Create($strQueue)
$q.SetPermissions($queueUser, [System.Messaging.MessageQueueAccessRights]::FullControl, [System.Messaging.AccessControlEntryType]::Set)
$q.SetPermissions("BUILTIN\Administrators", [System.Messaging.MessageQueueAccessRights]::TakeQueueOwnership, [System.Messaging.AccessControlEntryType]::Set)
Write-Host "... created $strQueue and set FullControl permissions for $queueUser"
}
}

I know this is an ancient question and has already been answered but I was struggling with this for far too long today and have to post my solution to spare others the pain.
If you don't have access to the MSMQ by default then you need to run the commands as an impersonated user as #Noon Silk suggested.
This code will let you create and assign permissions to the queues as a different user
$Username = "Eric"
$Password = "MyPassword"
$securePass = ConvertTo-SecureString $Password -AsPlainText -Force
$credential = New-Object Management.Automation.PSCredential($Username, $securePass)
foreach($strQueue in $strQueues) {
if (![System.Messaging.MessageQueue]::Exists($strQueue)) {
$script =
{
$q = [System.Messaging.MessageQueue]::Create($strQueue)
$q.SetPermissions($queueUser, [System.Messaging.MessageQueueAccessRights]::FullControl, [System.Messaging.AccessControlEntryType]::Set)
$q.SetPermissions("BUILTIN\Administrators", [System.Messaging.MessageQueueAccessRights]::TakeQueueOwnership, [System.Messaging.AccessControlEntryType]::Set)
Write-Host "... created $strQueue and set FullControl permissions for $queueUser"
}
Invoke-Command -Credential $credential -ScriptBlock $script
}
}

I think that taking ownership can be done only from native code ( with the c api of msmq). So no powershell here. But there is a c+ sample here.

You could try impersonating a relevant admin account before creating them ... ?

Related

Powershell incorrect running sequence

Kinda new to powershell and trying to write scripts in general. Im trying to create a script that creates an AD user and then assigns that user a license.
However doesn't seem to matter what I do, the sync command I have doesnt execute before the waiting period; so it cant find the user to assign the license to.
Any ideas what Im getting wrong?
`$DCSync = 'DC01'
#Starts AD Sync
Invoke-Command -ComputerName $DCSync -scriptblock {
Import-Module ADSync
Start-ADSyncSyncCycle -PolicyType Delta
Write-Output "testing"
}
send-mailmessage -From "abc#test123.co.uk" -To "abcHelpdesk#test123.co.uk" -Subject "New user creation" -Body "Please connect to DC01 and authenticate to Office 365 to complete the user setup for $UserPrincipalName" -SmtpServer [REDACTED]
Start-Countdown -Seconds 5 -Message "Synchronizing changes to Office 365"
#Install-Module PowerShellGet
#Install-Module Microsoft.Graph -Scope CurrentUser
#Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Connect-MgGraph -Scopes User.ReadWrite.All, Organization.Read.All
$MgUserID = Get-MgUser -UserId "$EmailAddress"
Update-MgUser -UserId "$MgUserID" -UsageLocation GB
Set-MgUserLicense -UserId $MgUserID -AddLicenses #{SkuId = "6fd2c87f-b296-42f0-b197-1e91e994b900" } -RemoveLicenses #()`
Write-Outpost "testing" always prints after the ADsync commands
Creating a user - assigning a license to newly created user
It just errors out because its not syncing to AD using the command so the user doesn't 'exist' yet
A couple of thoughts:
Try using Start-Sleep rather than Start-Countdown
If it isn't asynchronous, you can try running Start-ADSyncSyncCycle -PolicyType Delta using the -AsJob parameter, and then retrieve the status of that job using a while loop and not proceeding until the job is completed
If you have the e-mail address, then you can use a while loop to not proceed until the account is created, like:
while ($null -eq $MgUserID){
try {
$MgUserID = Get-MgUser -UserId "$EmailAddress"
}
catch {
$MgUserID = $null
}
Start-Sleep -Seconds 30
}

Powershell: Net.Webclient - not getting reply from intranet depending on machine

Cheers everyone,
I am getting the weirdest problem for which I need your helping ideas how to approach the issue.
So, I have a download script that pulls content off a company intranet using Webclient objects. It requires credentials and it is working on about 80% of the computers. The script pulls a listing using .DownloadString and then parses and gets some files using .DownloadFile.
On the machines that won't work the initial .DownloadString hangs until it appears to run into a timeout and returns $null.
User credentials are irrelevant on these types of machines meaning a user that works on another machine fails on this one.
Addresses, if entered into browser returns content.
Spoken in code I try it this way:
$wc = new-object System.Net.WebClient
$wc.Credentials = new-object System.Net.NetworkCredential($user, $pass, $domain)
$old_eap = $ErrorActionPreference
$ErrorActionPreference = "Stop"
try
{
$tmp = $wc.DownloadString($url)
if ([String]::IsNullOrEmpty($tmp))
{
throw "Intranet server did not return directory listing"
}
Return $tmp #the code is actually part of a function...
}
catch
{
write-error $_.Exception.Message
Return $null
}
finally
{
$ErrorActionPreference = $old_eap
}
I have no idea other than looking for changed settings between different machines. But which settings could be relevant for Webclient behaving like this? Any Ideas? I am seriously stuck...
I forgot... To make things a little easier I am stuck with Version 2.0 and we cant update yet. Bummer...
Thanks in advance
Alex
Maybe try to use xmlhttp as a client. Below is the usage example.
$url = "https://example.com/"
$http = New-Object -ComObject Msxml2.XMLHTTP
$user = "Domain\username"
$pwd = "password"
$utf = [System.Text.Encoding]::UTF8
$http.open("GET", $url, $false, $user, $pwd)
$http.send()
$result = $utf.GetString($http.responseBody)

JEA Invoke Command Issue

I have been working on implementing JEA into my environment to allow for our Jenkins System to not have local admin controls on all of our servers.
I have been able to create the JEA files, and specified commands that the user account can run.
The problem i am running into is that i am needing to pass a variable value from my Jenkins server to a remote server. So far i am not able to perform this.
Here is the code i am using with Powershell:
$session = New-PSSession -ComputerName "webaoneratebook" -ConfigurationName JEA-A1
$user = "USER"
$File = "C:\users\svc.jenkins.prd\Documents\WindowsPowerShell\FILE.txt"
$myCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user ,(Get-Content $file| ConvertTo-SecureString)
$pass = $MyCredentials.GetNetworkCredential().Password
Start-sleep -s 3
Invoke-Command -Session $session -scriptblock {AnalyzeRatebook} -ArgumentList $pass
Get-PSSession| Remove-PSSession
No matter what i have tried, i cannot seem to get the password to pass over.
In the JEA PSRC file, i have set the following remote variables:
VariableDefinitions = #{ Name = 'log'; Value = 'D:\inetpub\wwwroot\Jenkinstemp\A1Log.log'},
#{ Name = 'binpath'; Value = 'D:\inetpub\wwwroot\AscendantOne\bin' },
#{ Name = 'user'; Value = 'USER' },
#{ Name = 'pass2'; Value = 'TEMP' },
#{ Name = 'manifest'; Value = 'D:\inetpub\wwwroot\Jenkinstemp\manifest.xml' }
In the JEA PSRC file, i have made my function like this:
FunctionDefinitions = #{
'Name' = 'AnalyzeRatebook'
'ScriptBlock' = {
& $binpath\AOImportCmd.exe -u $user -p $pass -o $log -a $manifest
}
}
And made sure it is in Visible Functions as well.
VisibleFunctions = 'AnalyzeRatebook','TestRatebook',#{ Name = 'AnalyzeRatebook'; Parameters = #{ Name = 'ArgumentList'}}
If i do a Write-Host "& $binpath\AOImportCmd.exe -u $user -p $pass -o $log -a $manifest", I get back everything except the password.
So can anyone give some advice on how to get the local variable to translate over to a remote system when using JEA?
More Secure Resolution:
I worked a bit more and found a better answer that preserves the NoLanguage Mode of JEA. This is useful since ConstrainedLanguage Mode and FullLanguage Mode give too much power for modification even in JEA limited accounts.
In the end, i had to add two additional cmdlets to the VisibleCmdlets listing.
Get-Variable and Set-Variable.
Once i added these, I was able to set the system back to NoLanguage mode, and then pass my local variables to the remote host via the ICM command below:
Invoke-Command -Session $session -scriptblock {AnalyzeRatebook -u"$Using:user" -p"$Using:pass" -o"$Using:log" $Using:manifest}
Alternative Resolution
The problem was due to Three (edit: math is hard) reasons:
1. No-Language Mode was configured. This is modified in the PSSC file.
2. User error with how i defined Param in the PSRC file.
3. ICM needed to be changed in how it reached out.
Item 1:
To resolve, i went into the JEA-A1.PSSC file on the remote system, then (in this case, there was never a language mode defined) added a new line to the file called LanguageMode.
LanguageMode = 'FullLanguage'
Item 2
Then i updated the Custom Function that i wrote in the JEA-A1.PSRCfile with the correct syntax.
Original:
# Functions to define when applied to a session
FunctionDefinitions = #{
'Name' = 'AnalyzeRatebook'
'ScriptBlock' = {
Param($user),($pass),($log),($manifest)
& $binpath\AOImportCmd.exe "-a" $user $pass $log $manifest
$LASTEXITCODE
}
}
Corrected:
# Functions to define when applied to a session
FunctionDefinitions = #{
'Name' = 'AnalyzeRatebook'
'ScriptBlock' = {
Param($user,$pass,$log,$manifest)
& $binpath\AOImportCmd.exe "-a" $user $pass $log $manifest
$LASTEXITCODE
}
}
Item 3:
Finally the Invoke-Command call needed to be updated to accurately read and parse the variables. In my case, I could not set them to a single string as the system was expecting a pause in between the values to signify what they were.
Invoke-Command -Session $session -scriptblock {Param($user,$pass,$log,$manifest) AnalyzeRatebook -u"$user" -p"$pass" -o"$log" $manifest} -ArgumentList $user,$pass,$log,$manifest
Now this is working and passing the values correctly.

Maintaining user environment variables in Powershell logon script with admin privileges

I accidentally deleted 180 users from my AD and they aren't recoverable. I have recreated the accounts in AD and what not. This creates a new profile on their laptops when they login because of the new SID. I'm trying to write a script that grants them access to their old profile folder and create a shortcut on their desktop that leads there.
I've got the script working fine with one problem. The environment variables that are used, end up referring back to the admin account that runs the script. The users themselves don't have permission to change security on their old folder. I need to try and have the environment variables refer to the user yet have the privilege of an admin account to rewrite the permissions.
Here is the script so far.. I'm deploying this with Task Scheduler at the moment, which is another can of worms in that I'm not entirely understanding of the credential side of things there. I mean ideally, the task would run as a domain admin, execute the script asap, and have the script resolve the environment variables to the logged on user.
$permission = ":(OI)(CI)M"
$sam = $env:USERNAME
$folderName = "C:\Users\$($sam)"
Invoke-Expression -Command ( 'ICACLS $folderName /grant:r $sam$($permission) /t' )
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\Profile Backup.lnk")
$Shortcut.TargetPath = $folderName
$Shortcut.Save()
Its the $env:USERNAME and $home variables that are giving me trouble..
Or is there another way I should be tackling this problem?
You could use query session command to get the login name of the current logged on user. Then create NTAccount object based on that to retrieve SID and win32_userprofile WMI object to find out the profile path. Like this:
$m = query session | Select-String -Pattern "\>console\s*(\S*)\s"
$sam = $m.Matches[0].Groups[1].value
$acc = New-Object System.Security.Principal.NTAccount($sam)
$sid = $acc.Translate([System.Security.Principal.SecurityIdentifier]).Value
$profile = Get-CimInstance -ClassName win32_userprofile -Filter "SID='$sid'"
$folderName = $profile.LocalPath
Edit I have given it second thought over-night so I'll update the answer. You will be required to have domain admin password encrypted and then users will run the script.
It always sucks when something like this happens. I don't have a possibility to try this out, but I think the following approach would be feasible. The script asks user for password encrypts it and run the command as the user.
First phase would be to have a domain admin to encrypt his password to a file:
This is to be prepared by Domain Admin (distributed with the PS script) - I recommend changing password after the recovery is complete:
1) Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File 'C:\<script_path>\admin_passwd.txt'
2) This is to be executed by user (you have to fill in the admin user id and have the password file distributed with the script). The script path can be obtained by (Get-Location).Path. I'm not adding it into the source code so you can decide how to implement it:
$permission = ":(OI)(CI)M"
$admin= "<your_admin_userid>"
$sam = $env:USERNAME
$domain = $env:UserDomain
$folderName = "C:\Users\$($sam)"
# get domain admin password
$encrypted_passwd = get-content 'C:\<script_path>\admin_passwd.txt' | ConvertTo-securestring
# Setting process invocation parameters.
$process_start_info = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$process_start_info.CreateNoWindow = $true
$process_start_info.UseShellExecute = $false
$process_start_info.RedirectStandardOutput = $true
$process_start_info.RedirectStandardError = $true
$process_start_info.UserName = $admin
$process_start_info.Domain = $domain
$process_start_info.Password = $encrypted_passwd
$process_start_info.Verb = 'runas'
$process_start_info.FileName = 'ICACLS'
$process_start_info.Arguments = "$folderName /grant:r $sam$($permission) /t"
# Creating process object.
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo = $process_start_info
# Start the process
[Void]$process.Start()
$process.WaitForExit()
# synchronous output - captures everything
$output = $process.StandardOutput.ReadToEnd()
$output += $process.StandardError.ReadToEnd()
Write-Output $output
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\Profile Backup.lnk")
$Shortcut.TargetPath = $folderName
$Shortcut.Save()

PowerShell: Update Scheduled Task username and password?

I'm looking for a way to update the credentials on existing scheduled tasks on machines.
Schtasks doesn't work for AT created scheduled tasks
Win32_ScheduledJob only works for AT created jobs
Schedule.Service COM object - not sure
It appears that I can use RegisterTask and RegisterTaskDefinition to CREATE scheduled tasks but I'm not clear if I can update the existing credentials with those methods. Please advise. Thx.
I would recommend having a look at the managed TaskScheduler API, which is a .NET wrapper for the TaskScheduler COM API. It's an open-source project available on CodePlex.
http://taskscheduler.codeplex.com/
The project author has this to say about updating passwords:
If you are trying to create a task using the credentials of the
current user and you only want it to run when that user is logged in,
you need to call the RegisterTaskDefinition method as in the end of
the Complex example with the InteractiveToken parameter. If you need
to create as another specific user, then use that same method, but
supply the username, password, and set the TaskLogonType to
InteractiveTokenOrPassword or Password. There are some triggers that
are specific to a user, like the LogonTrigger where you can also
supply a user credential.
The appropriate overload for RegisterTaskDefinition is defined in TaskFolder.cs.
http://taskscheduler.codeplex.com/SourceControl/changeset/view/75611#19440
public Task RegisterTaskDefinition(string Path, TaskDefinition definition
, TaskCreation createType, string UserId, string password = null
, TaskLogonType LogonType = TaskLogonType.S4U, string sddl = null)
I had this task come up recently. I know this post is old but wanted to put this script somewhere for people to find hopefully help them. This is pure power shell so no extra setup required. I had tried using the Set-ScheduledTask but had issues setting domain user. I ended up using schtasks instead. You may need to run this script as administrator.
$domainUsername = "domain\user"
$password= "somePassword"
$tasks = Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq $domainUsername }
foreach ($task in $tasks) {
$myTask = $task.TaskPath+$task.TaskName
echo changing $myTask
schtasks /change /s $env:COMPUTERNAME /tn $myTask /ru $domainUsername /rp $password
}
Here is another example of how to push out to multiple servers. THe sessionCredentials are the credentials used to connect to the servers the domain user and new password are the name and password to be set to the tasks
$sessionCredentials = Get-Credential
$taskDomainUsername = "DOMAIN\SOMEACCOUNT"
$NewPassword = "somePassword"
$computerNames = #(
"someNameServer1",
"someNameServer2"
)
$serverSession = New-CimSession -ComputerName $computerNames -Credential $sessionCredentials
$tasks = Get-ScheduledTask -CimSession $serverSession | Where-Object { $_.Principal.UserId -eq $taskDomainUsername }
foreach($task in $tasks) {
echo updating $task
$specificSession = $serverSession | Where-Object { $_.ComputerName -eq $task.PSComputerName}
$taskFullName = $task.TaskPath+$task.TaskName
Set-ScheduledTask -CimSession $specificSession -TaskName $taskFullName -User $taskDomainUsername -Password $NewPassword
}