Question)
How do I get a DSC script resource to wait until the code has completed before moving on?
(The code is invoke-expression "path\file.exe")
Details)
I am using powershell version 5
and am trying to get DSC setup to handle our sql server installations.
My manager has asked me to use the out of the box DSC components.
i.e. no downloading of custom modules which may help.
I have built up the config file that handles the base server build - everything is good.
The script resource that installs sql server is good.
It executes, and waits until it has installed completely, before moving on.
When I get up to the script resource that installs the sql server cumulative update, I have issues.
The executable gets called and it starts installing (it should take 10-15 minutes), but the dsc configuration doesn't wait until it has installed, and moves on after a second.
This means that the DependsOn for future steps, gets called, before the installation is complete.
How can I make the script resource wait until it has finished?
Have you tried the keyword "DependsOn" like that ?
Script MyNewSvc
{
GetScript = {
$SvcName = 'MyNewSvc'
$Results = #{}
$Results['svc'] = Get-Service $SvcName
$Results
}
SetScript = {
$SvcName = 'MyNewSvc'
setup.exe /param
while((Get-Service $SvcName).Status -ne "Running"){ Start-Sleep 10 }
}
TestScript = {
$SvcName = 'MyNewSvc'
$SvcLog = 'c:\svc.log'
If (condition) { #like a a running svc or a log file
$True
}
Else {
$False
}
}
}
WindowsFeature Feature
{
Name = "Web-Server"
Ensure = "Present"
DependsOn = "[Script]MyNewSvc"
}
Invoke-Expression doesn't seem to wait until the process has finished - try this in a generic PowerShell console and you'll see the command returns before you close notepad:
Invoke-Expression -Command "notepad.exe";
You can use Start-Process instead:
Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow;
And if you want to check the exit code you can do this:
$process = Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow -PassThru;
$exitcode = $process.ExitCode;
if( $exitcode -ne 0 )
{
# handle errors here
}
Finally, to use command line arguments:
$process = Start-Process -FilePath "setup.exe" -ArgumentList #("/param1", "/param2") -Wait -PassThru;
$exitcode = $process.ExitCode;
Related
On Arch Linux I have a Windows 10 Guest on top of libvirt, kvm and virsh (still having some trouble to connect all these dots mentally together). Every time I suspend the laptop and a day is gone the Windows 10 host goes out of sync. I learned that with the following command I can force a time sync in the host:
➜ ~ virsh qemu-agent-command win10 '{"execute":"guest-set-time"}'
{"return":{}}
In order to make this work I modifed the clock XML block and added a kvm clock entry. This is how the block looks like now:
<clock offset="localtime">
<timer name="tsc" tickpolicy="delay"/>
<timer name="kvmclock"/>
<timer name="rtc" tickpolicy="delay" track="wall"/>
<timer name="pit" tickpolicy="delay"/>
<timer name="hpet" present="yes"/>
</clock>
I would like to know whether I can automate this step or trigger an update everytime I wake up the machine or log-in.
Thanks in advance
I was not able to get anywhere specifically using virsh. Here is how I fixed this issue in a Windows 11 guest on MacOS in UTM 3.6.4 and 4.1.5.
At first I tried many workarounds using w32tm - but this was always flaky.
This helped slightly:
disable "use local time for base clock" (otherwise you can't add a manual -rtc argument if using UTM)
add -rtc base=localtime,driftfix=slew
This wasn't great, because it won't recover a significant delta.
This is the solution I settled on (run in the Windows guest). It creates a scheduled task that runs every 5 minutes, gets the time from NTP, converts it to local time, measures the drift, and if the drift is >30 seconds in either direction it updates the system clock.
function Get-NtpTime
{
[OutputType([datetime])]
[CmdletBinding()]
param
(
[string]$Server = "time.nist.gov",
[int]$Port = 13
)
if (-not $PSBoundParameters.ContainsKey('ErrorAction'))
{
$ErrorActionPreference = 'Stop'
}
$Client = [Net.Sockets.TcpClient]::new($Server, $Port)
$Reader = [IO.StreamReader]::new($Client.GetStream())
try
{
$Response = $Reader.ReadToEnd()
$UtcString = $Response.Substring(7, 17)
$LocalTime = [datetime]::ParseExact(
$UtcString,
"yy-MM-dd HH:mm:ss",
[cultureinfo]::InvariantCulture,
[Globalization.DateTimeStyles]::AssumeUniversal
)
}
finally
{
$Reader.Dispose()
$Client.Dispose()
}
$LocalTime
}
function Register-TimeSync
{
[CmdletBinding()]
param
(
[Parameter()]
[timespan]$RepetitionInterval = (New-TimeSpan -Minutes 5),
[Parameter()]
[timespan]$ExecutionTimeLimit = (New-TimeSpan -Minutes 3)
)
$Invocation = {
$NtpTime = Get-NtpTime
$Delta = [datetime]::Now - $NtpTime
if ([Math]::Abs($Delta.TotalSeconds) -gt 30)
{
Set-Date $NtpTime
}
}
$PSName = if ($PSVersionTable.PSVersion.Major -le 5) {'powershell'} else {'pwsh'}
$Path = (Get-Command $PSName).Source
$Command = Get-Command Get-NtpTime
$Definition = "function Get-NtpTime`n{$($Command.Definition)}"
$Invocation = $Definition, $Invocation -join "`n"
$Bytes = [Text.Encoding]::Unicode.GetBytes($Invocation)
$Encoded = [Convert]::ToBase64String($Bytes)
$TriggerParams = #{
Once = $true
At = [datetime]::Today
RepetitionInterval = $RepetitionInterval
}
$Trigger = New-ScheduledTaskTrigger #TriggerParams
$Action = New-ScheduledTaskAction -Execute $Path -Argument "-NoProfile -EncodedCommand $Encoded"
$Settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit $ExecutionTimeLimit -MultipleInstances IgnoreNew
$Principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$RegisterParams = #{
TaskName = "Update system time from NTP"
Trigger = $Trigger
Action = $Action
Settings = $Settings
Principal = $Principal
Force = $true
}
Register-ScheduledTask #RegisterParams
}
Usage (run as admin):
Register-TimeSync
Hi im trying to make a power shell script that can automate the enable for the data refresh schedule. can anyone help me with that?
$rs2010 = New-WebServiceProxy -Uri "URL HERE" -Namespace
SSRS.ReportingService2010 -UseDefaultCredential;
$rs2010.Timeout = 3600000
$schedules = $rs2010.ListSchedules("URL HERE");
Write-Host "--- Disabled Schedules ---";
Write-Host "----------------------------------- ";
$schedules | WHERE { $_.ScheduleStatename -ne 'Ready' }
**strong text**
i have this that can output disabled schedules. i need help to make a powershell script that can enable the data refresh whenever its turn off.
/Adel
EDIT:::
so i got this code
$rs2010 = New-WebServiceProxy -Uri
"http://url here/_vti_bin/ReportServer/ReportService2010.asmx"
-Namespace SSRS.ReportingService2010 -UseDefaultCredential;
$subscription = $rs2010.ListSubscriptions("http://url here/")
| Where-Object {$_.ScheduleStatename -ne "Ready" } ;
ForEach ($subscription in $subscriptions)
{
$rs2010.EnableDatasource($subscription.SubscriptionID);
$subscription | select subscriptionid, report, path
}
but i get this error
Exception calling "EnableDataSource" with "1" argument(s): "The path of the item 'bda17ed4-81a5-40a6-bade-894ecde02373' is not valid. The full path must be less than 260 characters long;
other restrictions apply. If the report server is in native mode, the path must start with slash. ---> Microsoft.ReportingServices.Diagnostics.Utilities.InvalidItemPathException:
Objective: Robo copy from multiple machines on the network to a network share using variables for both the machine name and the currently logged on user.
What I have: txt file with a list of computernames.
Issue: I cannot get the foreach to work with the .split("\")[1] I use on the username variable to remove the domain prefix so I can use the output from that in the robocopy path
something like
robocopy "\\$computername\c$\documents and settings\$username\backup" "\\networkshare\backup\$username\backup"
gives me the error
You cannot call a method on a null-valued expression.
At C:\Scripts\Test\backup.ps1:13 char:2
Here's what I have so far. Can somebody help please?
function Get-LoggedIn {
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[string[]]$computername
)
foreach ($pc in $computername){
$logged_in = (gwmi win32_computersystem -COMPUTER $pc).username
$name = $logged_in.split("\")[1]
"{1}" -f $pc,$name
}
}
$computers = Get-Content "C:\Scripts\testcomputers.txt"
foreach ($computer in $computers) {
$users = Get-LoggedIn $computer
}
$SourceFolder = "\\$computer\c$\users\$users\desktop"
$DestinationFolder = "\\networkshare\backups\$users\backup\desktop"
$Logfile = "\\networkshare\backups\$users\backup\backuplog.txt"
Robocopy $SourceFolder $DestinationFolder /E /R:1 /W:1 /LOG:$Logfile
I see multiple errors here. You're not running the copy commands inside the foreach-loop. The username property recieved from WMI can often be in the following format:
domain\computer\username (or computer\domain\username, unsure since I'm on non-domain workstation now)
Anyways, the username is always the last part, so get it by using the index [-1] instead.
Updated script (with indents!):
function Get-LoggedIn {
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[string[]]$computername
)
foreach ($pc in $computername){
$logged_in = (gwmi win32_computersystem -COMPUTER $pc).username
$name = $logged_in.split("\")[-1]
"{1}" -f $pc,$name
}
}
$computers = Get-Content "C:\Scripts\testcomputers.txt"
foreach ($computer in $computers) {
$users = Get-LoggedIn $computer
$SourceFolder = "\\$computer\c$\users\$users\desktop"
$DestinationFolder = "\\networkshare\backups\$users\backup\desktop"
$Logfile = "\\networkshare\backups\$users\backup\backuplog.txt"
& Robocopy $SourceFolder $DestinationFolder /E /R:1 /W:1 /LOG:$Logfile
}
I have a .net application that successfully runs Powershell commands that it pulls from text files- until I tried doing one that is more complicated and contains an if condition. The script works correctly from a PS console but in .NET I only know how to pass in a string for the script, which after reading the file, it adds extra stuff like vblf and even if I take it out, it won't work. Is this even possible?
.NET Runtime Error: Server was unable to process request. ---> The
term 'False' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or
if a path was included, verify that the path is correct and try again.
.NET code:
'Grab Powershell script from text (.ps1) file
strScript = File.ReadAllText(ScriptFileName)
'inject the arguments into the script
strScript = InsertArguments(strScript, Arguments)
'Open the runspace and create a pipeline if it's not already open
If psRunspace.RunspaceStateInfo.State = RunspaceState.BeforeOpen Then
psRunspace.Open()
End If
Dim MyPipeline As Pipeline = psRunspace.CreatePipeline()
MyPipeline.Commands.AddScript(strScript)
Dim psResults As Collection(Of PSObject) = MyPipeline.Invoke()
Powershell Script, stored in ps1 file:
new-mailbox -name $argument1 -DisplayName $argument1 -UserPrincipalName $argument2 Room -DomainController $argument5
if ($argument4 -eq "False") {
Set-CalendarProcessing $argument1 -BookingWindowInDays 400 -DeleteSubject $false -AutomateProcessing autoaccept -AllBookInPolicy $false -BookInPolicy $argument3 -DomainController $argument5
} else {
Set-CalendarProcessing $argument1 -BookingWindowInDays 400 -DeleteSubject $false -AutomateProcessing autoaccept -AllBookInPolicy $true -DomainController $argument5
}
When this script is read in, here is a substring of what gets pulled into strScript:
-Room -DomainController mcexdct1" & vbLf & "if (False -eq "False") {"
Fixed it by changing the PowerShell script from this:
if ($argument4 -eq "False") {
to this:
if ("$argument4" -eq "False") {
It works with quotation marks around it. I guess the "junk" I was seeing in the script string (like vblf) is normal- I don't work with reading from text files that much.
Hi i am not exactly sure if my wording is right but i need a variable which contains current date/time whenever i write data to log ; how can i do that without initializing everytime.Currently everytime i need a update i use these both statements jointly.Is there an other way of doing this?
$DateTime = get-date | select datetime
Add-Content $LogFile -Value "$DateTime.DateTime: XXXXX"
please do let me know if any questions or clarifications regarding my question.
This script make the real Dynamic variable in Powershell ( Thanks to Lee Holmes and his Windows PowerShell Cookbook The Complete Guide to Scripting Microsoft's Command Shell, 3rd Edition)
##############################################################################
##
## New-DynamicVariable
##
## From Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Creates a variable that supports scripted actions for its getter and setter
.EXAMPLE
PS > .\New-DynamicVariable GLOBAL:WindowTitle `
-Getter { $host.UI.RawUI.WindowTitle } `
-Setter { $host.UI.RawUI.WindowTitle = $args[0] }
PS > $windowTitle
Administrator: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
PS > $windowTitle = "Test"
PS > $windowTitle
Test
#>
param(
## The name for the dynamic variable
[Parameter(Mandatory = $true)]
$Name,
## The scriptblock to invoke when getting the value of the variable
[Parameter(Mandatory = $true)]
[ScriptBlock] $Getter,
## The scriptblock to invoke when setting the value of the variable
[ScriptBlock] $Setter
)
Set-StrictMode -Version 3
Add-Type #"
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
namespace Lee.Holmes
{
public class DynamicVariable : PSVariable
{
public DynamicVariable(
string name,
ScriptBlock scriptGetter,
ScriptBlock scriptSetter)
: base(name, null, ScopedItemOptions.AllScope)
{
getter = scriptGetter;
setter = scriptSetter;
}
private ScriptBlock getter;
private ScriptBlock setter;
public override object Value
{
get
{
if(getter != null)
{
Collection<PSObject> results = getter.Invoke();
if(results.Count == 1)
{
return results[0];
}
else
{
PSObject[] returnResults =
new PSObject[results.Count];
results.CopyTo(returnResults, 0);
return returnResults;
}
}
else { return null; }
}
set
{
if(setter != null) { setter.Invoke(value); }
}
}
}
}
"#
## If we've already defined the variable, remove it.
if(Test-Path variable:\$name)
{
Remove-Item variable:\$name -Force
}
## Set the new variable, along with its getter and setter.
$executioncontext.SessionState.PSVariable.Set(
(New-Object Lee.Holmes.DynamicVariable $name,$getter,$setter))
There's a Set-StrictMode -Version 3 but you can set it as -Version 2 if you can load framework 4.0 in your powershell V2.0 session as explained Here
The use for the OP is:
New-DynamicVariable -Name GLOBAL:now -Getter { (get-date).datetime }
Here the Lee Holmes's evaluation (where it is clear what is the real flaw) about the method I used in my other answer:
Note
There are innovative solutions on the Internet that use PowerShell's debugging facilities to create a breakpoint that changes a variable's value whenever you attempt to read from it. While unique, this solution causes PowerShell to think that any scripts that rely on the variable are in debugging mode. This, unfortunately, prevents PowerShell from enabling some important performance optimizations in those scripts.
Why not use:
Add-Content $LogFile -Value "$((Get-Date).DateTime): XXXXX"
This gets the current datetime every time. Notice that it's inside $( ) which makes powershell run the expression(get the datetime) before inserting it into the string.
wrap your two commands in function so you will have just one call ?
function add-log{
(param $txt)
$DateTime = get-date | select -expand datetime
Add-Content $LogFile -Value "$DateTime: $txt"
}
Besides these other ways (which frankly I would probably use instead - except the breakpoint approach), you can create a custom object with a ScriptProperty that you can provide the implementation for:
$obj = new-object pscustomobject
$obj | Add-Member ScriptProperty Now -Value { Get-Date }
$obj.now
Using PsBreakPoint:
$act= #'
$global:now = (get-date).datetime
'#
$global:sb = [scriptblock]::Create($act)
$now = Set-PSBreakpoint -Variable now -Mode Read -Action $global:sb
calling $now returns current updated datetime value
One liner:
$now = Set-PSBreakpoint -Variable now -Mode Read -Action { $global:now = (get-date).datetime }