Currently, I have the following script loaded in the user data.
$username = "ABC"
$Password = ConvertTo-SecureString -AsPlainText -String "xxx" -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $Password
Start-Process -FilePath "powershell" -Credential $credential -WorkingDirectory "C:\Test" -ArgumentList #(".\script.ps1") -Wait
However, it seems that the script.ps1 is not being executed. There are no errors or outputs. It just doesn't work. The script only works when it is executed in the instance itself and not when during instance launch and the user data is invoked.
Update:
The purpose of the script.ps1 is to return a text file that shows what user has executed the script.ps1. So, the intended output after executing the user data would be a text file which is not the case.
Script.ps1
([Environment]::UserDomainName + "" + [Environment]::UserName) | out-file test.txt
"$env:userdomain$env:username" | out-file -append test.txt
[Security.Principal.WindowsIdentity]::GetCurrent().Name | out-file -append test.txt
Related
I'm trying to rename hostname and add to AD of a spot instance. It is a simple powershell script. I've read the docs that by default user data will be disable after it gets executed once and if <persist>true</persist> is used it will not be disabled.
I think I saw somewhere this(enabling to be run at each startup) is done via taskscheduler but can't find the link.
Can someone point me to the task scheduler job or the way to manually disable the userdata once my if conditions are met.
<powershell>
Set-ExecutionPolicy unrestricted -Force
$instanceName = "test-name5"
$username = "domain\username"
$password = "password" | ConvertTo-SecureString -AsPlainText -Force
$cred = New-Object -typename System.Management.Automation.PSCredential($username, $password)
Start-Sleep -s 5
$hostname = hostname
$domain = (Get-WmiObject win32_computersystem).Domain
if (!($hostname -eq $instanceName)){
Rename-Computer -NewName $instanceName -restart -force
}Elseif (!($domain -eq 'my.domain.local')){
Start-Sleep -s 5
Add-Computer -DomainName my.domain.local -OUPath "OU=Windows,OU=QAServers,OU=Servers,DC=my,DC=domain,DC=local" -Credential $cred -Force -Restart -erroraction 'stop'
}Else {
####code to disable the running of userdata once above conditions
are met####
}
</powershell>
<persist>true</persist>
It's worth reading the ec2config-service documentation, as the setting you want is referenced in there.
You want the Ec2HandleUserData setting, which is configured in the Config.xml.
Powershell can easily update this setting:
$path = 'C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml'
$xml = [xml](Get-Content $path)
$state = $xml.Ec2ConfigurationSettings.Plugins.Plugin | where {$_.Name -eq 'Ec2HandleUserData'}
$state.State = 'Disabled'
$xml.Save($path)
I use this code when creating custom AMI's to re-enable userdata handling ($state.State = 'Enabled').
EDIT: The above is for ec2config not ec2launch which is what the OP is using. I'd missed this originally.
I this case I think you need to change the way your script runs, rather than use <persist> and then try to disable its functionality, I would remove the persist tag and call InitializeInstance.ps1 –Schedule (documentation link) in your if for the conditions you want the userdata to re-run:
if ($hostname -ne $instanceName) {
& C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule
Rename-Computer -NewName $instanceName -Restart -Force
}
elseif ($domain -ne 'my.domain.local') {
& C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule
Add-Computer -DomainName aws.macmillan.local -OUPath "OU=Windows,OU=QAServers,OU=Servers,DC=my,DC=domain,DC=local" -Credential $cred -Force -Restart -ErrorAction 'stop'
}
As I said in the comments of the previous answer, I had 3 options and since I found the aws scheduled task I went with the last option. Answering my own question since it'll be easy to spot the code.
<powershell>
Set-ExecutionPolicy unrestricted -Force
#Enter instance hostname here
$instanceName = "test-name8"
$username = "domain\username"
#Using ssm parameter store to avoid having the password in plaintext
$password = (Get-SSMParameterValue -Name AD-Password -WithDecryption $True -Region us-east-1).Parameters[0].Value | ConvertTo-SecureString -asPlainText -Force
Start-Sleep -s 3
$cred = New-Object -typename System.Management.Automation.PSCredential($username, $password)
Start-Sleep -s 5
$hostname = hostname
$domain = (Get-WmiObject win32_computersystem).Domain
if ($hostname -ne $instanceName){
Rename-Computer -NewName $instanceName -restart -force
}Elseif ($domain -ne 'my.domain.local'){
Start-Sleep -s 5
Add-Computer -DomainName my.domain.local -OUPath "OU=Windows,OU=QAServers,OU=Servers,DC=my,DC=domain,DC=local" -Credential $cred -Force -Restart -erroraction 'stop'
}Else {
Disable-ScheduledTask -TaskName "Amazon Ec2 Launch - Userdata Execution"
Unregister-ScheduledTask -TaskName "Amazon Ec2 Launch - Userdata Execution"
}
</powershell>
<persist>true</persist>
note: a role that has ssm policies must be attached while launching the server for this ssm parameter command to work.
I was solving similar issue and I had to change Windows Server 2016 hostname and enroll it to Elastic Server Fleet. Also I could not allow instance to be rebooted. I used this code to solve this.
NB. I understand that it is not direct way of doing this and has numerous drawbacks, but in my circumstances goal was achieved without negative impact.
<powershell>
$ComputerName = "MyPCRandomName"
Set-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" -name "Hostname" -value $ComputerName
elastic-agent enroll --enrollment-token 123 --url=321
</powershell>
I have a script that I can double click and it'll open other scripts as admin. Works with some things but not everything. For one script, it opens the next window and then immediately closes it. For another, I get this error:
At MYPATH\InstallClient.ps1:33 char:78
+ ... tall_x64.msi" -force -recurse -ErrorAction Stop #Cleans out the file ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The string is missing the terminator: ".
At MYPATH\InstallClient.ps1:27 char:31
+ ForEach ($entry in $computers){ #start of foreach loop
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
Below is the script to open a script as an admin:
Function Get-FileName($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "PS1 (*.ps1)| *.ps1"
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile = Get-FileName "MYPATH\Scripts"
powershell.exe -noprofile -command "&{start-process powershell -ArgumentList '-NoExit -noprofile -file $inputfile' -verb RunAs}"
This is the script that it gives the previous error for while trying to open:
Function Get-FileName($initialDirectory) #Function to choose a file
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "MSI (*.msi)| *.msi" #type of files that will be available for selection
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile = Get-FileName "MyPath" #Directory that is going to open to select a file from
Function Get-FileName($initialDirectory) #Function to choose a file
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "CSV (*.csv)| *.csv" #type of files that will be available for selection
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile1 = Get-FileName "MyPath\ServerLists"
$computers = import-csv $inputfile1
ForEach ($entry in $computers){ #start of foreach loop
$computername = $entry.computernames #this saves the single entry under computernames for each entry in csv file
Copy-item $inputfile -container -recurse \\$computername\C$\windows\temp #this copies the msi file that we selected to the computer entry called from the csv file's temp folder
Invoke-Command -Computername $computername –ScriptBlock {Start-process -Wait "C:\windows\temp\ShadowSuiteClientInstall_x64.msi"} | out-null #This starts the msi file that we just copied and waits for the installation to be completed before moving on
If($?){ #If the last command was successful
Echo "Installed ShadowSuiteClientInstall_x64 on $computername."
Remove-Item "\\$computername\C$\windows\temp\ShadowSuiteClientInstall_x64.msi" -force -recurse -ErrorAction Stop #Cleans out the file we copied into the temp folder
}
}
Does anyone have any ideas on why this will open some things fine but give this error for this script and immediately close other scripts without running them? Does anyone have a better way to navigate through scripts and select one to open as admin?
Ok I figured this out. I loaded the script into powershell ISE and I saw that it was compiling it incorrectly. It kept turning the -Scriptblock into an ae symbol instead of the - in front of scriptblock. Weird AF IMO but ok, I fixed it in ISE, which I recommend to anyone struggling with weird compiling errors like this.
I've been trying to use Powershell with imported PowerCLI commands for VMware administration and I've hit a snag. What I'm trying to do is query all VM's in a location (doesn't matter where), and for every VM I want the group with "Virtual Machine User with Snapshot" permission, and then use that group name to run a Get-ADGroupMembers query for everyone in that group. I also have to remove the domain prefix from the AD query, which would otherwise cause an error.
After some more playing around with outputting hash table information into the csv, as opposed to 'SystemObject[]', I finally got the script so it doesn't return errors EXCEPT on VM's where there is more than one group. It throws an error but the script continues, and just outputs the members of the first group.
How do I get it to do a recursive AD query for every group that is pulled into the owner groups hashtable? The output would be the same as for all the other VM's, just with a line for each group and members.
$AllVMs = #()
$vms = get-vm * -Location datacenter
foreach ($vm in $vms)
{
$owners = Get-VIPermission $vm.name | where-object {$_.role -eq "virtual machine user with snapshot"}
foreach ($owner in $owners)
{
$members = Get-ADGroupMember ($owners.principal -replace '^prefix\\')
$temp = New-Object psobject |
Add-Member Noteproperty "Name" -value $vm.name -PassThru |
Add-Member Noteproperty "Owner" -value (#($owners.principal) -join ',') -PassThru |
Add-Member Noteproperty "Members" -value (#($members.SamAccountName) -join ',') -passthru
$AllVMs+=$temp
}
$AllVMs | Export-Csv -Path c:\users\me\desktop\AllVMs.csv
I was playing around with it some more today and figured it out! I'm running the script right now against a datacenter with 350+ machines so technically I don't know 100% that it works, but it worked against 3 machines :-) I also added a line to list every machine that's owned by more than one group - handy for troubleshooting. Here's the script:
$AllVMs = #()
$vms = get-vm -Location DATACENTER
foreach ($vm in $vms)
{
$owners = #(Get-VIPermission $vm.name | where-object {$_.role -eq "virtual machine user with snapshot"})
if ($owners.count -gt 1) {write-host "** Note ** '$vm' has"$owners.count "owner groups"}
foreach ($owner in $owners)
{
$members = Get-ADGroupMember ($owner.principal -replace '^prefix\\')
$temp = New-Object psobject |
Add-Member Noteproperty "Name" -value $vm.name -PassThru |
Add-Member Noteproperty "Owner" -value (#($owner.principal) -join ',') -PassThru |
Add-Member Noteproperty "Members" -value (#($members.SamAccountName) -join ',') -PassThru
$AllVMs+=$temp
}
}
$AllVMs
Change $owners.principal to $owner.principal after $members = Get-ADGroupMember? In case you have nested AD groups, Get-ADGroupMember has a -Recursive parameter.
I am trying to write a simple script that stops and then starts a process (Application).
I can stop it fine, but can't find a way to start it again.
The string to start the process should be: "c:\AppFolder\AppName.exe" instance1
my script is:
$appName = "AppName.exe"
$filter = "name like '%"+$appName+"%'"
$result = Get-WmiObject win32_process -Filter $filter
$processid = $result.ProcessId
$command = $result.CommandLine
stop-process $processid
start $command
If I run $result | select * I see that there is an item for CommandLine which is "C:\AppFolder\AppName.exe" instance1
But If I try and do:
$command = $result.CommandLine
stop-process $processid
start $command
I get start-process : This command cannot be executed due to the error: The system cannot find the file specified
But if I manually type into a powershell window start "c:\AppFolder\AppName.exe" instance1 the Application starts fine.
Am I missing something here?
(n.b. it was suggested to me in "powershell v2 - how to get process ID" that I could use
$processid = get-process appName | select -expand id to get the processid, but when I expanded this to get all the items (probably not the correct term?)
in the object I couldn't see an option for CommandLine or similar)
I found the following (but still doesn't work)
$command = Get-WmiObject win32_process -Filter $filter | select -expandproperty CommandLine
write-host $command
This writes "c:\AppFolder\AppName.exe" instance1
start-process $command
But this then results in the following error:
Start-Process : This command cannot be executed due to the error: The system cannot find the file specified
.
At line:11 char:14
+ start-process <<<< $command
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
However, running:
start-process "c:\AppFolder\AppName.exe" instance1
starts the application?
I think I've solved it!! (and it's quite simple really) After much googling. . .
Apparently the start-process cmdlet only accepts a file location.
In order to add an argument (in this case the instance name) I need to use the attribute -ArgumentList
So I need to get the CommandLine item, and split it up, then pass it back in two parts
e.g.
$result = Get-WmiObject win32_process -Filter $filter
$comLine = $result.CommandLine -split"( )"
$comm = $commLine[0]
$inst = $commLine[2]
start-process -FilePath $comm -ArgumentList $inst
And this works as I expected it to.
Hi i am new to power shell and i cant seem to get this script to run it is to remote execute a command using the command prompt on a computer in a workgroup here is the error i get the script is below i am running the script on a win 7 machine the machine i want to remote execute on is windows xp sp3 the fire wall is off and the com settings are set for default for authentication settings and identify for impersonate and help would be great
here is the Error
Invoke-WmiMethod : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
At C:\Users\Kevin\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1:57 char:40
+ $newproc = Invoke-WmiMethod <<<< -class Win32_process -name Create `
+ CategoryInfo : NotSpecified: (:) [Invoke-WmiMethod], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.InvokeWmiMethod
$command = Read-Host " Enter command to run"
$user = "\Administrator"
$Domainname = $HostName +$user
$login = Get-Credential $domainname
[string]$cmd = "CMD.EXE /C " +$command
}
process {
$newproc = Invoke-WmiMethod -class Win32_process -name Create `
-ArgumentList ($cmd) -EnableAllPrivileges -ComputerName $HostName -authentication Packetprivacy -Impersonation 3 -Credential $login
if ($newproc.ReturnValue -eq 0 )
{ Write-host -foregroundcolor Green "Command $($command) Ran Sucessfully on $($HostName)"}
I think this is because Get-Credential won't pass the password to the -credential of Invoke-WmiMethod. I do this exact thing by creating the credential password using "convertto-securestring"
I know it's not as secure as you have to put the password in as plain text, but if you're the only one using the script to do maintenance or such..it's no biggie.
Try this:
$command = Read-Host " Enter command to run"
$pass = ConvertTo-SecureString "yourpassword" -Force -AsPlainText
$Domainname = 'Domain'
$user = '\administrator'
$login = $Domainname + $user
$cred = (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $login, $pass)
[string]$cmd = "CMD.EXE /C " +$command
}
process {
$newproc = Invoke-WmiMethod -class Win32_process -name Create `
-ArgumentList ($cmd) -EnableAllPrivileges -ComputerName $HostName -authentication Packetprivacy -Impersonation 3 -Credential $cred
if ($newproc.ReturnValue -eq 0 )
{ Write-host -foregroundcolor Green "Command $($command) Ran Sucessfully