GCE Windows startup scripts & naming - powershell

Linux admin pulling out what hair I have left trying to figure out startup scripts for Windows on GCE.
The way I've approached it is the following;
Create a "runonce" script by editing the registry on the "master" image.
Said script does the following;
-
Does a reverse DNS lookup of it's own IP to get the hostname DNS thinks it is and then sets the local hostname to that
Joins the domain
Adds a domain user as autologin (I need this for various reasons)
I GCESysprep the machine
Take an image of the machine after sysprep and make a group template from that
My main issue I'm having is that it's not working at all :) The machines come up, run the script and reboot but god knows what state they're in after they come back, I can't login, reset the password/do anything.
I think ideally what I'd like to do is figure out the best way of doing this, do I host the script in GCE Storage and mark it a startup script in the GCE Console opposed to the registry setting? Is there a better way of renaming the machines?
Here's the script if you're interested;
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
$ipAddresses = (get-netadapter | get-netipaddress | ? addressfamily -
eq 'IPv4').ipaddress
# Filter out any 192.168 172. 10. IP addresses
$ipAddress = $ipAddresses -like "123*"
# Retrieve the hostname from the ??? DNS Server
$fqdn = (nslookup $ipAddresses | sls name | select -last
1).toString().split(":")[1].trim()
# We only need the hostname without the domain info so split it up
$fqdn_items = $fqdn.split(".")
$newComputerName = $fqdn_items[0]
Write-Host "New Computer Name: $newComputerName"
# Get a WMI object representing the current computer
$currentComputer = Get-WmiObject Win32_ComputerSystem
Write-Host "Attempting to change computer name to $newComputerName"
# Set the Computer Name to the hostname found via DNS Lookup to DNS
Server
# This can only be performed before joining the domain otherwise you
get return code 1326
$currentComputer.Rename($newComputerName)
#SET CREDENTIALS
$domain = “mydomain”
$password = “password” | ConvertTo-SecureString -asPlainText -Force
$username = "$domain\joinuser”
$credential = New-Object
System.Management.Automation.PSCredential($username,$password)
# RENAME THE COMPUTER
Rename-Computer -ComputerName (hostname) -NewName $newComputerName -
LocalCredential $credentiallocal
sleep 100
# JOIN THE DOMAIN
Add-Computer -DomainName $domain -Credential $credential -force
# CONFIGURE AUTOLOGIN
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows
NT\CurrentVersion\Winlogon' -Name AutoAdminLogon -Value 1
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows
NT\CurrentVersion\Winlogon' -Name DefaultUserName -Value
“mydomain\dr-worker"
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows
NT\CurrentVersion\Winlogon' -Name DefaultPassword -Value mypassword
restart

Related

copy files from network share while running a remediation script in Intune with alternate credentials

Using Intune I beleive the problem is I can't access the Network share from the SYSTEM account, so really asking if, as a remediation script in INTUNE, there is any way to run the script as SYSTEM but invoke commands via script block as another user (with permissions to network share
I need to copy a file from the network share to the C:\windows\temp folder, then install the software on the local machine using the proactive remediation script in Microsoft Intune
$FSPath = "C:\Program Files (x86)\Freshdesk\Freshservice Discovery Agent"
$serviceName = 'FSDiscoveryAgent'
$tempPath = "c:\windows\temp"
$FSService = Get-Service -Name $serviceName
if(Test-Path $FSPath -eq $false){
Write-Output "Freshservice agent not installed on workstation."
$password = ConvertTo-SecureString "hello1" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("myUsername",
$password)
$alternateUsers = [scriptblock]{
Copy-Item -Path (Join-Path -Path "\\myShare.com\util\software\FreshService\2.9 Agent" -ChildPath "fs-windows-agent-2.9.0.msi") `
-Destination "$tempPath\fs-windows-agent-2.9.0.msi"
}
Invoke-Command -ScriptBlock $alternateUsers -Credential $Cred
}else{
Write-Output "Freshservice agent is installed"
}
I would suggest to use regular Intune Win32 app deployment capabilities for this and not abuse proactive remediations for this task. Storing credentials within a script is considered bad practice and contents will be logged plain text in the Intune management extension log.
Win 32 apps provide detection rules, you could test if the service is present on the device - if not install the agent otherwise it's considered as installed or add additional logic to check for a specific version.
https://learn.microsoft.com/en-us/mem/intune/apps/apps-win32-add, https://github.com/Microsoft/Microsoft-Win32-Content-Prep-Tool/releases/latest
This is working code that allows me to use another account when installing software. I will try in intune tomorrow
$password = ConvertTo-SecureString "p#ssw0rd" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("domain.com\adminAccount", $password)
$alternateUsers = [scriptblock]{
Copy-Item -Path (Join-Path -Path "\\sharedDrive.com\util\software\FreshService\2.9 Agent" -ChildPath "fs-windows-agent-2.9.0.msi") -Destination "C:\windows\temp\fs-windows-agent-2.9.0.msi"
}
# https://www.itdroplets.com/run-a-command-as-a-different-user-in-powershell/
$GetProcessJob = Start-Job -ScriptBlock $alternateUsers -Credential $Cred
Wait-Job $GetProcessJob
$GetProcessResult = Receive-Job -Job $GetProcessJob
Write-Output $GetProcessResult
if($GetProcessResult.state -eq "Completed"){
Start-Process "C:\windows\temp\fs-windows-agent-2.9.0.msi" -ArgumentList "/i /qn"
}

Invoke-Command doesn't return to local machine but software is installed

The tl;dr: I'm puzzled as to why my script isn't returning back to the deployment machine. Does anyone have an idea why?
I was provided an EXE and a couple arguments to install some software. I'm using PowerShell's Start-Process to run it. The deployment machine is Win Server 2012 domain controller, logged in and being run as domain admin. The test machines are two Windows 10 Pro domained machines. All three have PS 5.1.
Being a silent install, there is no user interaction required. If I run the exact command locally, via RDP, or in a pssession, with just c:\Software.exe /silent /arg2removed, it installs and returns as expected.
The script runs fine up to a point. Nothing happens after Start-Process inside Invoke-Command 's -ScriptBlock. In a separate PowerShell window, I can use Enter-PSSession for each of the two client machines, and Get-Service and Get-Process both show the software's service and background processes, respectively. I can Ctrl+c on the deployment machine and get back to a prompt. No errors are reported at any time.
Here's the Start-Process chunk. I've read the help and it doesn't sound like I'm missing anything that would allow the ScriptBlock to finish. If I prepend Start-Process with Write-Host (like we all do), it echoes the command that would run and I get back to a command prompt on the deployment machine.
# Start the installer.
Start-Process `
-FilePath "C:\${using:SrcExe}" `
-ArgumentList "/SILENT", "/arg2removed" `
-WorkingDirectory C:\ `
-Wait `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InstallErrors
Here's most of the script. The only items before Invoke-Command are where I set up $ComputersToInstallOn, enter the credentials (yes I'm sure they're correct), and supply the path to the EXE.
Invoke-Command `
-ComputerName $ComputersToInstallOn `
-Credential $Creds `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InvokeCommErrors `
-ScriptBlock {
# Get and print the destination machine's hostname
$ThisMachine = Get-Content Env:\COMPUTERNAME ; $ThisMachine
# Print the current date and time
Get-Date
# Check if Sentinel processes are running. If not, assume it's not installed.
$S1Procs = get-process sentinel*
if([string]::IsNullOrEmpty($S1Procs)) {
# Sentinel isn't installed. Continue.
# Map a drive letter to $SrcFolder. Not theoretically necessary but Start-Process complains when copying with the UNC path directly.
New-PSDrive `
-Name S `
-PSProvider FileSystem `
-Credential ${using:Creds} `
-Root ${using:SrcFolder} `
-verbose
# List remote folder
Get-ChildItem S:\
# Copy the $SrcExe to C:\
Copy-Item `
-Path "S:\${using:SrcExe}" `
-Destination C:\ `
-Verbose `
-ErrorAction Stop `
-ErrorVariable CopyErrors
# Unmount drive
Remove-PSDrive S -verbose
# Verify EXE exists locally
Get-ChildItem -Path C:\${using:SrcExe}
# If there were copy errors, abort.
if ($CopyErrors) {
Write-Host "There was an error copying '${using:SrcExe}' to $ThisMachine. Aborting."
exit 1 } else {
# All good so far. Continue to install.
Write-Host "$(Get-Date -UFormat '%Y%m%d %H:%M:%S') : Starting install on ${ThisMachine}. You may need to Ctrl+C to return to the local machine. Check processes on each machine though."
# Start the installer.
Start-Process `
-FilePath "C:\${using:SrcExe}" `
-ArgumentList "/SILENT", "/arg2removed" `
-WorkingDirectory C:\ `
-Wait `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InstallErrors
# ScriptBlock doesn't seem to make it to anything after Start-Process.
# Remove the EXE.
Remove-Item "C:\${using:SrcExe}" -Verbose -ErrorAction SilentlyContinue
exit 0
# Get-Process -Name Sentinel*
# echo "Sleeping. Now would be the time to abort."
# Start-Sleep 15
}
} else {
Write-Host "Sentinel appears to be installed and running."
$S1Procs
Get-Service -Name Sentinel* | Where-Object { $_.Status -match "Running" }
exit 0
}
}
if($InvokeCommErrors){
Write-Host "There were some errors."
}
EDIT: Added some requested info.

PowerShell - Invoke Command on Remote Machines/Computers

There's plenty of forums/material regarding the subject line, but cannot seem to get an answer for my problem.
I'm trying to execute a script from the main server (SRV01) that will clean the temp folders on the secondary servers (SRV02, SRV03).
Here is the script:
#Set the machines on the network to run the script on
$VDST = #("SRV02", "SRV03")
#Folder locations to clean out
$TempFolder = #("C:\Windows\Temp\*", "C:\Documents and Settings\*\Local Settings\temp\*")
#This function actually performs the clean up operation
Function executeCleanUp
{
$TempFolder = $args[0]
$machineNames = $args[2]
ForEach($machine in $machineNames){
Get-PSSession -ComputerName $machine | Format-Table -Property ComputerName, InstanceID
Write-Host 'Starting Clean Up...'
#Loop through the sub folders in the registry location
ForEach($folderLocation in $TempFolder)
{
$StrInput = 'Remove-Item -Path ' + $folderLocation + ' -Force -Recurse -ErrorAction SilentlyContinue'
$action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument $StrInput
Register-ScheduledTask -Action $action -TaskName "CleanUp"
Start-ScheduledTask -TaskName "CleanUp"
Unregister-ScheduledTask -TaskName "CleanUp" -Confirm:$false -ErrorAction SilentlyContinue
}
}
#Execute Script on specified machines - provided in array above
Invoke-Command -ComputerName $VDST -ScriptBlock ${function:executeCleanUp} -ArgumentList $TempFolder, $VDST
After running the above, I get the error:
A specified logon session does not exist
So, I came across a forum where it was suggested to do the following:
#Remote Server (VDI)
Enable-WSManCredSSP -Role server
#Expected Output
#This computer is configured to receive credentials from a remote client computer.
#Local Machine
Enable-WSManCredSSP -Role Client -DelegatedCredentials 'SRV01'
#Expected Output
#The machine is configured to allow delegating fresh credentials to the following target(s): wsman/SRV01.
#Local Machine
#Open gpedit.msc
#Browse to Computer Configuration > Administrative Templates > System > Credentials Delegation.
#Double-click "Allow delegating fresh credentials with NTLM-only Server Authentication"
#Enable the setting
#Add the build server to the server list as WSMAN/BuildServerName.
#Example Execution:
#Invoke-Command -ComputerName <REMOTE_COMPUTER_NAME> -Authentication CredSSP -Credential <USERNAME> -ScriptBlock { #code}
I've done all this, but now I get the error:
A computer policy does not allow the delegation of the user
credentials to the target computer
Also, I am assuming the line
WSMAN/BuildServerName
should be written
WSMAN/SRV02
The 2 hop authentication issue came up because you are trying to list remote sessions with in your remote session
Get-PSSession -ComputerName $machine | Format-Table -Property ComputerName, InstanceID
If you just want to clear some files on the remote servers the code below should work with no need for CredSPP.
Setting -ErrorAction SilentlyContinue will make trouble shooting difficult, It's easier to check if the file exists before you try to delete it.
$TempFolder = $args[0]
$ComputerArray = "SRV02","SRV03"
$ScriptBlock =
{
foreach ($Folder in $TempFolders)
{
if (Test-Path -Path $TempFolder)
{
Remove-Item -Path $Folder -force
}
}
}
Invoke-Command -ComputerName $ComputerArray -ScriptBlock $ScriptBlock -ArgumentList $TempFolder
Wrong answer:
Your issue is two hop authentication.
You can't nest remote sessions with default windows settings.
While this would not be considered to be best practice, you can enable CredSSP to bypass the problem.
https://learn.microsoft.com/en-us/windows/win32/secauthn/credential-security-support-provider
https://learn.microsoft.com/en-us/powershell/module/microsoft.wsman.management/enable-wsmancredssp?view=powershell-7
Could you either log on to SVR01 to run the script or run the script against the target machines from your own computer?

Why am i receiving RPC server is unavailable error when looping?

I have a Powershell script to find specific servers and their corresponding service accounts. If I modify the script to use a single server and a single service account, the results are what I expect. If I loop thru the servers and accounts, I receive the following error:
#################################################################
# Find Service Account(s) used to start Services on a Server(s) #
#################################################################
$accounts = (Get-Content C:\Users\location\Scripts\Service_Accounts.txt)
Remove-Item -path C:\Users\location\Scripts\ServiceAccountFnd.txt -force -erroraction silentlycontinue
Import-Module ActiveDirectory # Imports the Active Directory PowerShell module #
## Retrieves servers in the domain based on the search criteria ##
$servers=Get-ADComputer -Filter {Name -Like "namehere*"} -property *
## For Each Server, find the services running under the user specified in $account ##
ForEach ($server in $servers) {
Write-Host $server
ForEach ($account in $accounts) {
Write-Host $account
Get-WmiObject Win32_Service -ComputerName $server | Where-Object {$_.StartName -like "*$account*"} | Format-Table -HideTableHeaders -property #{n='ServerName';e={$_.__SERVER}}, StartName, Name -AutoSize | Out-File -FilePath C:\Users\location\Scripts\ServiceAccountFnd.txt -append -Width 150
}
}
Your $server variable does not only contain the hostname, but also all attributes of the AD computer object.
Try to change the ComputerName value to $server.name.
If that doesn't help: Can you confirm, that you used the very same computer in the loop as without the loop, as you described? I'd assume that you try to access another computer, which is not configured as expected.
Besided that, I'd recommend you to use Get-CimInstance rather than Get-WmiObject, as it doesn't use RPC, but WinRM by default. WinRM is more firewall friendly, secure and faster.

Setting VLAN from CSV File in PowerCLI Script

I am trying to compile a PowerCLI script to help automate the creation of VMs from a CSV file's data. Currently the script I am running looks like this:
# Specify vCenter Server, vCenter Server username and vCenter Server user password
write-host “Please specify vCenter Server and enter credentials” -foreground green
$vc = read-Host "Connect to which vCenter Server?"
write-host “Connecting to vCenter Server $vc” -foreground green
$CSVPath = "$ScriptRoot\PROD_VMRequestTEST.xlsx.csv"
Connect-VIServer -Server $vc
$CSVFile = Import-Csv -Path $CSVPath
Import-Csv -Path $CSVPath
# Specify vCenter Server Virtual Machine & Templates folder
$Folder = “Discovered virtual machine”
#
# Specify the vSphere Cluster
$Cluster = 'vSphere 5.5 RND'
$esx = Get-Cluster $Cluster | Get-VMHost -State connected
Import-Csv -Path $CSVPath | %{
Write-Host “Creation of VM $_.Name initiated” -ForegroundColor green
New-VM -Name $_.Name -VMHost ($esx | Get-Random) -Location $Folder
Write-Host “Power On of the VM $_.name initiated” -ForegroundColor green
Start-VM -VM $_.Name -confirm:$false -RunAsync
}
I have encountered a few examples online dealing with setting the VLAN via PowerCLI but none of them seem to relate directly to my example of using the CSV file to determine the VLAN.
One example I tried using with my script was:
$VMhost = Get-vmhost $_.VMHost
$PortGroup = Get-VirtualPortgroup -name $_.VLAN -VMhost $VMhost
However, I get errors relating to the -Name argument in that line.
On the script context, I believe the error is causing because one of the columns value seems empty(in your case its Name). I can see that you are getting all the values in a foreach loop. I would recommend you to create a sample csv, insert only 2 set of data and see if it works. This way atleast you can narrow down the issue.
Hope this helps...!!!