dsccommunity / xPSDesiredStateConfiguration

DSC resources for configuring common operating systems features, files and settings.
https://dsccommunity.org
MIT License
209 stars 133 forks source link

xService: How to upgrade new versions of a service #65

Open AceHack opened 8 years ago

AceHack commented 8 years ago

If I try to combine this with File resource I get file in use errors, I'm trying to figure out the correct workflow for upgrading services. I need to stop\uninstall the service, copy new files, then install\start the service. Is there any way to accomplish this with xSevice and combining other resources, or am I going to have to write my own custom resource. Thanks.

AceHack commented 8 years ago

Here is an example of my config

    Node localhost
    {
        File EnsureLatestServiceExist {
            Ensure = 'Present'
            Type = 'File'
            Checksum = 'ModifiedDate'
            SourcePath = '\\BuildMachine\Output\MyService.exe'
            DestinationPath = 'c:\MyService\MyService.exe'
        }

        xService EnsureServiceStarted {
            Ensure = 'Present'
            DependsOn = '[File]EnsureLatestServiceExist'
            Name = 'MyService'
            DisplayName = 'My Service'
            Description = 'My Service'
            Path = 'c:\MyService\MyService.exe'
            StartupType = 'Automatic'
            State = 'Running'
        }
    }
kwirkykat commented 8 years ago

Have you tried adding Force = $true to the File resource?

If that doesn't work, you'll probably have to write a Script resource before the File resource that will check if the file is locked. We have a function getting added in #159 in CommonTestHelper.psm1 under the tests folder called Test-IsFileLocked that tests for a locked file because of this same error.

redoz commented 8 years ago

We're running into the same issue. Forcing the file copy wouldn't solve the problem as we need to cleanly shut down the service as well.

We tried xService ShutDownAndUninstallService File CopyServiceFiles xService InstallAndStartService

But DSC (understandably) doesn't let you configure two conflicting states like that.

We made a custom xUninstallService resource to work around the problem but doesn't feel "right" as our config now contains explicitly contradicting states (The service obviously can't be uninstalled and installed at the same time). I don't know enough about DSC to know whether this will become a problem, if it ever were to simply "test" the config against current state it would never succeed.

So the other solution is to duplicate all the effort of xService and File in a custom resource but that doesn't seem sensible either. It could be that I'm missing the big picture but xService doesn't really seem all that thought out in its current form, but I'll admit that I don't know how to make it more useful either.

AceHack commented 8 years ago

@redoz I agree xService is not well designed, it only has the capability to do a fresh install of a brand new service. When it comes to updating a service you are on your own.

I wrote a custom file copy that would check for file changes then kill the service before it did the copy if there was file changes. Then xService would change the service state back to started once it got to that point in the script.

Just in general I find many of the PowerShell DSC scripts on GitHub only think about initial install and give no thought to upgrade, this seems like a very common gap in most of the DSC scripts I've seen on GitHub.

Even though I'm heavy using DSC, I mostly had to write my own DSC modules because the one's here on GitHub are not "Production Ready" by virtue of the fact that they completely forget the upgrade scenario.

To be fair there are a few DSC packages that seem much more robust and well thought out like the SQL Server, and Active Directory related ones, so it's not all of them.

AceHack commented 8 years ago

The biggest problem I see is to solve the problem of a Service Upgrade if I were to take every DSC script Microsoft has released and try to put them together in some way to solve the problem it would not be possible.

1 This seems very weird given that upgrading a service is so command and such a easy thing to do.

2 So after getting over my initial frustration of nothing in dsc land being able to upgrade a service, I decide to write my own. This also feels incredibly wrong because to accomplish my task I have to copy logic either for copying files or installing a service from existing dsc resources.

So overall even though I have my own dsc resource for upgrading services, everything still feels wrong about it.

redoz commented 8 years ago

I think the problem is that we want to specify a sequence of events, and DSC is all about state, I guess that disconnect could explain why it doesn't "fit".

So if we assume it has to be a resource, could we then have a resource that simply uses Invoke-DscResource to orchestrate the sequence of events we're after? Or would that not be a good solution?

kwirkykat commented 8 years ago

The current xService resource checks if the source file's path has changed not if the source file's content has changed, so even if the file lock was not an issue, I don't think the service would update if only the contents of the file was changed at the same path.

The in-box Service resource won't update even if the path is changed.

So the only solution available right now is to extract the archive/ create a file at a new path and then switch the xService resource path. This will update the xService every time DSC runs though instead of just when the service has actually been updated.

I can mark this as an enhancement functionality that should be added to xService, but I don't have an immediate fix available right now.

redoz commented 8 years ago

@kwirkykat I still don't get it though, we can't replace the service executable while it's in use right? So how will checking the content fix that? And even if we could forcibly replace the files in use, how will we ensure a clean shutdown of the service?

kwirkykat commented 8 years ago

@redoz It wouldn't fix the issues with replacing the service executable. The file-content-check issue is another problem on top of the issue with replacing the executable.

This scenario requires a significant design enhancement to xService which would have to incorporate both being able to edit the source executable while the service is running and checking that that executable has been changed.

In order to have this update functionality , xService would have to be changed so that it creates a copy of the executable to run. Then when DSC detects that the content of the original executable that is not running has been changed, the resource would stop the service, remove the old executable copy, replace it with a copy of the updated executable, and then start the service again with the updated executable. That leaves the original file free to be tampered with if you would like to update it while the service is running.

This would be entirely new functionality for xService that is not in the current version. The best you can do to update the executable in the current version is to change the path of the service to point to a new executable which currently happens here in WriteBinaryProperties.

redoz commented 8 years ago

@kwirkykat Would creating my own service installer resource using Invoke-DscResource be a supported path forward? It would let me reuse the current resources, and I would have complete control over the steps needed: Stop Service, Uninstall Service, Copy Files, Update Configuration Files, Install Service, Start Service.

kwirkykat commented 8 years ago

@redoz Sure. You could create a fork of this repository and make these changes to the resource there and then open a pull request to add those changes to the xService resource (our instructions for contributing to this repository are here). Or you could just use your fork for your own resources with your own changes. Or you could make your own resource. Or you could write a PowerShell function that does this on a scheduled basis. Lots of possibilities :+1:

redoz commented 8 years ago

@kwirkykat Sorry, let me rephrase: is using Invoke-DscResource within a script resource acceptable or is that something you would not recommend doing? (Edit: Or was that what the "Sure" was for?)

Given that DSC is all about describing state, I don't think it will be possible to write a generic xService that does everything that everyone will possibly need with services. What if you need to run a maintenance script after shutting it down but before starting the new version. I don't see how you can represent that with state alone?

kwirkykat commented 8 years ago

@redoz Invoke-DSCResource won't work inside a Script resource in a configuration. The DSC engine can only run one configuration/resource at a time. I tried it out and got this error:

Cannot invoke the Invoke-DscResource cmdlet. The Start-DscConfiguration cmdlet is in progress and must return before Invoke-DscResource can be invoked. Use -Force option if that is available to cancel the current operation.
    + CategoryInfo          : NotSpecified: (root/Microsoft/...gurationManager:) [], CimException
    + FullyQualifiedErrorId : MI RESULT 1,Microsoft.PowerShell.DesiredStateConfiguration.Commands.InvokeDscResourceMethodCommand
    + PSComputerName        : localhost

It recommends a 'Force' parameter to override the original call to run the configuration, but there is no 'Force' parameter.

You could import MSFT_xServiceResource.psm1 and/or MSFT_xArchiveResource.psm1 as modules in a Script resource though and use the same Get/Set/Test functions that way.

redoz commented 8 years ago

@kwirkykat We'll try the work-around and report back, but it would be nice if DSC had an option where you could reuse resources while ignoring the the "State" part of the equation. Then we could simply use xService twice in sequence while flagging the first one as "This is not actually part of the configuration". Not sure what the best way would to implement this though, maybe some kind of "sequential composite resource" or simply another property on all resources (like DependsOn), maybe we could have IgnoreState or `ExcludeFromConfigurationState``.

I sort of see two solutions to this kind of problem:

  1. make a custom service installer and only use DSC to invoke it
  2. make a resource that is the service installer

We opted for #2 as it seemed like the least amount of work (all the relevant resources are already available: File, xService, etc). But as you cannot just use them without exposing their individual states we were hoping Invoke-DSCResource could be used, not exactly ideal but seemed like it would be good enough.

redoz commented 8 years ago

@kwirkykat I think the only way to make xService truly useful is to give it a source and a destination path and implement something like this:

  1. If contents of destination does not equal contents of source
    1. Ensure service is stopped
    2. Uninstall service
    3. Copy files to make destination match source
    4. Install service
  2. If service state doesn't match configuration
    1. Set service state accordingly

This lets us put the service binaries in a "staging" location, where we can ensure all the configurations are applied, then we can use xService to make sure the current service instance is up to date.

One thing to take into consideration would be files created at runtime by the service, for instance maybe it creates a log folder/files in its install dir which shouldn't be included as part of the file comparison between the source and destination paths.

This does mean we would end up duplicating a lot of the File resource behavior, but I don't see how else you would make xService a component that can be reliably used to install/update windows services.

Edit: This wont actually work, you cannot use DSC to put files in a location and then apply changes to those files. On Test the resources that put the files in the location will report false as they are different from what they were (due to config changes) so everything will get re-applied over and over.

kwirkykat commented 8 years ago

@TravisEz13 @KarolKaczmarek Any thoughts on changing the schema for xService resource to work like this so it can be updated with DSC?

nabru commented 7 years ago

Hi all, I just bumped into this issue as I would like to achieve exactly the same. I began to play with DSC a few month ago (and I'm really impressed of it) and would love to see that such scenario is covered out of the box. I thought that DSC is also designed to cover "Continuous Delivery" ?!

Is there a solution for this kind of deployment?

Cheers Stephan

CJHarmath commented 7 years ago

@kwirkykat I feel like this needs more attention as it's been a year now and this is not that uncommon use case in the Windows world. Also, someone should address @redoz 's suggestion of being able to have and IgnoreState flag and if possible or necessary go back to whiteboarding for a next DSC version if it can evolve to accomodate these scenarios as well ( if it didn't happen already )

There can be valid usecases when you would need to stop, upgrade, cleanup, start backup which is not possible with the current design or at least not nicely. A desired state could mean a need for multiple steps with certain state transitions, so allowing that would be very much welcomed. Also I feel like there should really be a way to send events of dependent configuraiton resource changes so you can do some processing and arrive to your desired state....

I found this as one of the biggest limitations of DSC right now

Also as a general feedback for product managers: Would be awesome if these things would not left abadoned for a year

There is also a ~1 years old related user voice left there with no real answer

https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/11088639-enable-service-restart-and-similar-scenarios-in-ds

Thanks

kwirkykat commented 7 years ago

@zjalexander

DanielBMann9000 commented 6 years ago

An IgnoreState flag would be fantastic. The desired state I'm trying to describe is simply this:

That's all. I can't describe that state at the moment, because it tries to start the service, which I don't care about -- there's a totally separate deployment pipeline that handles updating the binaries and enabling/disabling the service as appropriate.

johlju commented 6 years ago

@DanielBMann9000 if you set the parameter $State to 'Ignore', does your scenario work then?