private async Task <PrepareResult> PrepareResourceAsync(
            string label, Resource res, bool custom,
            ResourceArgs args, ResourceOptions options)
        {
            /* IMPORTANT!  We should never await prior to this line, otherwise the Resource will be partly uninitialized. */

            // Before we can proceed, all our dependencies must be finished.
            var type = res.GetResourceType();
            var name = res.GetResourceName();

            Log.Debug($"Gathering explicit dependencies: t={type}, name={name}, custom={custom}");
            var explicitDirectDependencies = new HashSet <Resource>(
                await GatherExplicitDependenciesAsync(options.DependsOn).ConfigureAwait(false));

            Log.Debug($"Gathered explicit dependencies: t={type}, name={name}, custom={custom}");

            // Serialize out all our props to their final values.  In doing so, we'll also collect all
            // the Resources pointed to by any Dependency objects we encounter, adding them to 'propertyDependencies'.
            Log.Debug($"Serializing properties: t={type}, name={name}, custom={custom}");
            var dictionary = await args.ToDictionaryAsync().ConfigureAwait(false);

            var(serializedProps, propertyToDirectDependencies) =
                await SerializeResourcePropertiesAsync(label, dictionary).ConfigureAwait(false);

            Log.Debug($"Serialized properties: t={type}, name={name}, custom={custom}");

            // Wait for the parent to complete.
            // If no parent was provided, parent to the root resource.
            Log.Debug($"Getting parent urn: t={type}, name={name}, custom={custom}");
            var parentURN = options.Parent != null
                ? await options.Parent.Urn.GetValueAsync().ConfigureAwait(false)
                : await GetRootResourceAsync(type).ConfigureAwait(false);

            Log.Debug($"Got parent urn: t={type}, name={name}, custom={custom}");

            string?providerRef = null;

            if (custom)
            {
                var customOpts = options as CustomResourceOptions;
                providerRef = await ProviderResource.RegisterAsync(customOpts?.Provider).ConfigureAwait(false);
            }

            // Collect the URNs for explicit/implicit dependencies for the engine so that it can understand
            // the dependency graph and optimize operations accordingly.

            // The list of all dependencies (implicit or explicit).
            var allDirectDependencies = new HashSet <Resource>(explicitDirectDependencies);

            var allDirectDependencyURNs = await GetAllTransitivelyReferencedCustomResourceURNsAsync(explicitDirectDependencies).ConfigureAwait(false);

            var propertyToDirectDependencyURNs = new Dictionary <string, HashSet <string> >();

            foreach (var(propertyName, directDependencies) in propertyToDirectDependencies)
            {
                allDirectDependencies.AddRange(directDependencies);

                var urns = await GetAllTransitivelyReferencedCustomResourceURNsAsync(directDependencies).ConfigureAwait(false);

                allDirectDependencyURNs.AddRange(urns);
                propertyToDirectDependencyURNs[propertyName] = urns;
            }

            // Wait for all aliases. Note that we use 'res._aliases' instead of 'options.aliases' as
            // the former has been processed in the Resource constructor prior to calling
            // 'registerResource' - both adding new inherited aliases and simplifying aliases down
            // to URNs.
            var aliases       = new List <string>();
            var uniqueAliases = new HashSet <string>();

            foreach (var alias in res._aliases)
            {
                var aliasVal = await alias.ToOutput().GetValueAsync().ConfigureAwait(false);

                if (uniqueAliases.Add(aliasVal))
                {
                    aliases.Add(aliasVal);
                }
            }

            return(new PrepareResult(
                       serializedProps,
                       parentURN ?? "",
                       providerRef ?? "",
                       allDirectDependencyURNs,
                       propertyToDirectDependencyURNs,
                       aliases));
        }
        /// <summary>
        /// Calls <see cref="ReadOrRegisterResourceAsync"/> then completes all the
        /// <see cref="IOutputCompletionSource"/> sources on the <paramref name="resource"/> with
        /// the results of it.
        /// </summary>
        private async Task CompleteResourceAsync(
            Resource resource, bool remote, Func<string, Resource> newDependency, ResourceArgs args,
            ResourceOptions options, ImmutableDictionary<string, IOutputCompletionSource> completionSources)
        {
            // Run in a try/catch/finally so that we always resolve all the outputs of the resource
            // regardless of whether we encounter an errors computing the action.
            try
            {
                var response = await ReadOrRegisterResourceAsync(
                    resource, remote, newDependency, args, options).ConfigureAwait(false);

                completionSources[Constants.UrnPropertyName].SetStringValue(response.urn, isKnown: true);
                if (resource is CustomResource)
                {
                    // ReSharper disable once ConstantNullCoalescingCondition
                    var id = response.id ?? "";
                    completionSources[Constants.IdPropertyName].SetStringValue(id, isKnown: id != "");
                }

                // Go through all our output fields and lookup a corresponding value in the response
                // object.  Allow the output field to deserialize the response.
                foreach (var (fieldName, completionSource) in completionSources)
                {
                    if (fieldName == Constants.UrnPropertyName || fieldName == Constants.IdPropertyName)
                    {
                        // Already handled specially above.
                        continue;
                    }

                    // We process and deserialize each field (instead of bulk processing
                    // 'response.data' so that each field can have independent isKnown/isSecret
                    // values.  We do not want to bubble up isKnown/isSecret from one field to the
                    // rest.
                    if (response.data.Fields.TryGetValue(fieldName, out var value))
                    {
                        if (!response.dependencies.TryGetValue(fieldName, out var dependencies))
                        {
                            dependencies = ImmutableHashSet<Resource>.Empty;
                        }

                        var converted = Converter.ConvertValue(err => Log.Warn(err, resource), $"{resource.GetType().FullName}.{fieldName}", value,
                            completionSource.TargetType, dependencies);
                        completionSource.SetValue(converted);
                    }
                }
            }
            catch (Exception e)
            {
                // Mark any unresolved output properties with this exception.  That way we don't
                // leave any outstanding tasks sitting around which might cause hangs.
                foreach (var source in completionSources.Values)
                {
                    source.TrySetException(e);
                }

                throw;
            }
            finally
            {
                // ensure that we've at least resolved all our completion sources.  That way we
                // don't leave any outstanding tasks sitting around which might cause hangs.
                foreach (var source in completionSources.Values)
                {
                    // Didn't get a value for this field.  Resolve it with a default value.
                    // If we're in preview, we'll consider this unknown and in a normal
                    // update we'll consider it known.
                    source.TrySetDefaultResult(isKnown: !_isDryRun);
                }
            }
        }