Exemple #1
0
        /// <summary>
        /// Format the type name of a CIM property in a presentable way.
        /// </summary>
        /// <param name="prop"></param>
        /// <param name="isOptionalProperty"></param>
        /// <returns></returns>
        private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, bool isOptionalProperty)
        {
            string cimTypeName = prop.TypeConstraint;
            StringBuilder formattedTypeString = new StringBuilder();

            if (string.Equals(cimTypeName, "MSFT_Credential", StringComparison.OrdinalIgnoreCase))
            {
                formattedTypeString.Append("[PSCredential]");
            }
            else if (string.Equals(cimTypeName, "MSFT_KeyValuePair", StringComparison.OrdinalIgnoreCase) || string.Equals(cimTypeName, "MSFT_KeyValuePair[]", StringComparison.OrdinalIgnoreCase))
            {
                formattedTypeString.Append("[Hashtable]");
            }
            else
            {
                string convertedTypeString = System.Management.Automation.LanguagePrimitives.ConvertTypeNameToPSTypeName(cimTypeName);
                if (!string.IsNullOrEmpty(convertedTypeString) && !string.Equals(convertedTypeString, "[]", StringComparison.OrdinalIgnoreCase))
                {
                    formattedTypeString.Append(convertedTypeString);
                }
                else
                {
                    formattedTypeString.Append("[" + cimTypeName + "]");
                }
            }

            // Do the property values map
            if (prop.ValueMap != null && prop.ValueMap.Count > 0)
            {
                formattedTypeString.Append(" { " + string.Join(" | ", prop.ValueMap.Keys.OrderBy(x => x)) + " }");
            }

            // We prepend optional property with "[" so close out it here. This way it is shown with [ ] to indication optional
            if (isOptionalProperty)
            {
                formattedTypeString.Append("]");
            }

            formattedTypeString.Append("\n");

            return formattedTypeString;
        }
Exemple #2
0
        private StatementAst ConfigurationStatementRule(IEnumerable<AttributeAst> customAttributes, Token configurationToken)
        {
            //G  configuration-statement:
            //G      'configuration'   new-lines:opt  singleNameExpression  new-lines:opt statement-block
            //G  singleNameExpression:
            //G      command-argument
            //G      primary-expression

            IScriptExtent startExtent = configurationToken.Extent;
            IScriptExtent endErrorStatement = startExtent;
            bool isError = false;

            // The expression that returns the configuration name.
            ExpressionAst configurationName;
            string simpleConfigurationNameValue = null;

            SkipNewlines();

            Token configurationNameToken = NextToken();
            Token configurationKeywordToken = configurationNameToken;

            if (configurationNameToken.Kind == TokenKind.LCurly)
            {
                ReportError(After(startExtent), () => ParserStrings.MissingConfigurationName);

                // Try reading the configuration body - this should keep the parse in sync - but we won't return it
                ScriptBlockExpressionRule(configurationNameToken);
                return null;
            }

            if (configurationNameToken.Kind == TokenKind.EndOfInput)
            {
                UngetToken(configurationNameToken);

                ReportIncompleteInput(After(configurationNameToken.Extent),
                                      () => ParserStrings.MissingConfigurationName);
                return null;
            }

            // Unget the configuration token so it can possibly be re-read as part of an expression
            UngetToken(configurationNameToken);

            // Finally read the name for this configuration
            configurationName = GetWordOrExpression(configurationNameToken);
            if (configurationName == null)
            {
                isError = true;
                ReportIncompleteInput(configurationNameToken.Extent, () => ParserStrings.MissingConfigurationName);
            }
            else
            {
                object outValue;
                if (IsConstantValueVisitor.IsConstant(configurationName, out outValue))
                {
                    simpleConfigurationNameValue = outValue as string;
                    if (simpleConfigurationNameValue == null ||
                        !System.Text.RegularExpressions.Regex.IsMatch(simpleConfigurationNameValue, "^[A-Za-z][A-Za-z0-9_./-]*$"))
                    {
                        // This is actually a semantics check, the syntax is fine at this point.
                        // Continue parsing to get as much information as possible
                        isError = true;
                        ReportError(configurationName.Extent, () => ParserStrings.InvalidConfigurationName, simpleConfigurationNameValue ?? string.Empty);
                    }
                }
            }

            SkipNewlines();

            //
            // Load the system classes and import them as keywords
            //
            Runspaces.Runspace localRunspace = null;
            bool topLevel = false;
            try
            {
                // At this point, we'll need a runspace to use to hold the metadata for the parse. If there is no
                // current runspace to use, we create one and set it to be the default for this thread...
                if (Runspaces.Runspace.DefaultRunspace == null)
                {
                    localRunspace =
                        Runspaces.RunspaceFactory.CreateRunspace(Runspaces.InitialSessionState.CreateDefault2());
                    localRunspace.ThreadOptions = Runspaces.PSThreadOptions.UseCurrentThread;
                    localRunspace.Open();
                    Runspaces.Runspace.DefaultRunspace = localRunspace;
                }

                // Configuration is not supported on ARM or in ConstrainedLanguage
                if (PsUtils.IsRunningOnProcessorArchitectureARM() || Runspace.DefaultRunspace.ExecutionContext.LanguageMode == PSLanguageMode.ConstrainedLanguage)
                {
                    ReportError(configurationToken.Extent,
                                () => ParserStrings.ConfigurationNotAllowedInConstrainedLanguage,
                                configurationToken.Kind.Text());
                    return null;
                }

                // Configuration is not supported on WinPE
                if (Utils.IsWinPEHost())
                {
                    ReportError(configurationToken.Extent,
                                () => ParserStrings.ConfigurationNotAllowedOnWinPE,
                                configurationToken.Kind.Text());
                    return null;
                }

                ExpressionAst configurationBodyScriptBlock = null;

                // Automatically import the PSDesiredStateConfiguration module at this point.
                PowerShell p = null;

                // Save the parser we're using so we can resume the current parse when we're done.
                var currentParser = Runspaces.Runspace.DefaultRunspace.ExecutionContext.Engine.EngineParser;
                Runspaces.Runspace.DefaultRunspace.ExecutionContext.Engine.EngineParser = new Parser();

                try
                {
                    if (localRunspace != null)
                    {
                        p = PowerShell.Create();
                        p.Runspace = localRunspace;
                    }
                    else
                    {
                        p = PowerShell.Create(RunspaceMode.CurrentRunspace);
                    }

                    try
                    {
                        // See of the default CIM keywords are already loaded. If they haven't been
                        // then this is the top level. Record that information and then load the defaults
                        // keywords.
                        if (DynamicKeyword.GetKeyword("OMI_ConfigurationDocument") == null)
                        {
                            // Load the default CIM keywords
                            Collection<Exception> CIMKeywordErrors = new Collection<Exception>();
                            Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors);

                            // Report any errors encountered while loading CIM dynamic keywords.
                            if (CIMKeywordErrors.Count > 0)
                            {
                                ReportErrorsAsWarnings(CIMKeywordErrors);
                            }

                            // Load any keywords that have been defined earlier in the script.
                            if (_configurationKeywordsDefinedInThisFile != null)
                            {
                                foreach (var kw in _configurationKeywordsDefinedInThisFile.Values)
                                {
                                    if (!DynamicKeyword.ContainsKeyword(kw.Keyword))
                                    {
                                        DynamicKeyword.AddKeyword(kw);
                                    }
                                }
                            }

                            topLevel = true;
                        }
                    }
                    catch (Exception e)
                    {
                        // This shouldn't happen - the system classes should always be good, but just in case,
                        // we'll catch the exception and report it as an error.
                        ReportError(configurationKeywordToken.Extent, () => e.ToString());
                        return null;
                    }
                }
                finally
                {
                    if (p != null)
                    {
                        p.Dispose();
                    }

                    //
                    // Put the parser back...
                    //
                    Runspaces.Runspace.DefaultRunspace.ExecutionContext.Engine.EngineParser = currentParser;
                }


                Token lCurly = NextToken();
                if (lCurly.Kind != TokenKind.LCurly)
                {
                    ReportIncompleteInput(After(lCurly.Extent), () => ParserStrings.MissingCurlyInConfigurationStatement);
                    isError = true;
                    UngetToken(lCurly);
                }
                else
                {
                    var oldInConfiguration = _inConfiguration;
                    try
                    {
                        _inConfiguration = true;
                        configurationBodyScriptBlock = ScriptBlockExpressionRule(lCurly);
                    }
                    finally
                    {
                        _inConfiguration = oldInConfiguration;
                    }
                    if (configurationBodyScriptBlock == null)
                    {
                        ReportError(After(lCurly.Extent), () => ParserStrings.ConfigurationBodyEmpty);
                        return null;
                    }
                }

                if (isError)
                {
                    return new ErrorStatementAst(ExtentOf(startExtent, endErrorStatement), configurationToken);
                }


                #region "Add Configuration Keywords"

                // If the configuration name is a constant string, then
                // if we're not at the top level, we'll add it to the list of configuration resource keywords.
                // If we are at the top level, then we'll add it to the list of keywords defined in this
                // parse so it can be used as a resource in subsequent config statements.
                var scAst = configurationName as StringConstantExpressionAst;
                if (scAst != null)
                {
                    var keywordToAddForThisConfigurationStatement = new System.Management.Automation.Language.DynamicKeyword
                    {
                        BodyMode = DynamicKeywordBodyMode.Hashtable,
                        ImplementingModule = _keywordModuleName,
                        Keyword = scAst.Value,
                        NameMode = DynamicKeywordNameMode.NameRequired,
                        DirectCall = true,
                    };

                    // Add the DependsOn property.
                    var dependsOnProp = new DynamicKeywordProperty
                    {
                        Mandatory = true,
                        Name = "DependsOn",
                    };
                    keywordToAddForThisConfigurationStatement.Properties.Add(dependsOnProp.Name, dependsOnProp);

                    // Add the PsDscRunAsCredential property.
                    var RunAsProp = new DynamicKeywordProperty
                    {
                        Mandatory = true,
                        Name = "PsDscRunAsCredential",
                    };
                    keywordToAddForThisConfigurationStatement.Properties.Add(RunAsProp.Name, RunAsProp);

                    // Extract the parameters, if any and them to the keyword definition.
                    var sbeAst = configurationBodyScriptBlock as ScriptBlockExpressionAst;
                    if (sbeAst != null)
                    {
                        var pList = sbeAst.ScriptBlock.ParamBlock;
                        if (pList != null)
                        {
                            foreach (var parm in pList.Parameters)
                            {
                                var keywordProp = new DynamicKeywordProperty();
                                keywordProp.Name = parm.Name.VariablePath.UserPath;
                                if (parm.Attributes != null)
                                {
                                    foreach (var attr in parm.Attributes)
                                    {
                                        var typeConstraint = attr as TypeConstraintAst;
                                        if (typeConstraint != null)
                                        {
                                            keywordProp.TypeConstraint = typeConstraint.TypeName.Name;
                                            continue;
                                        }
                                        var aAst = attr as AttributeAst;
                                        if (aAst != null)
                                        {
                                            if (string.Equals(aAst.TypeName.Name, "Parameter", StringComparison.OrdinalIgnoreCase))
                                            {
                                                if (aAst.NamedArguments != null)
                                                {
                                                    foreach (var na in aAst.NamedArguments)
                                                    {
                                                        if (string.Equals(na.ArgumentName, "Mandatory", StringComparison.OrdinalIgnoreCase))
                                                        {
                                                            if (na.ExpressionOmitted)
                                                            {
                                                                keywordProp.Mandatory = true;
                                                            }
                                                            else if (na.Argument != null)
                                                            {
                                                                ConstantExpressionAst ceAst = na.Argument as ConstantExpressionAst;
                                                                if (ceAst != null)
                                                                {
                                                                    keywordProp.Mandatory = System.Management.Automation.LanguagePrimitives.IsTrue(ceAst.Value);
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                keywordToAddForThisConfigurationStatement.Properties.Add(keywordProp.Name, keywordProp);
                            }
                        }
                    }

                    if (topLevel)
                    {
                        if (_configurationKeywordsDefinedInThisFile == null)
                        {
                            _configurationKeywordsDefinedInThisFile = new Dictionary<string, DynamicKeyword>();
                        }

                        _configurationKeywordsDefinedInThisFile[keywordToAddForThisConfigurationStatement.Keyword] = keywordToAddForThisConfigurationStatement;
                    }
                    else
                    {
                        System.Management.Automation.Language.DynamicKeyword.AddKeyword(keywordToAddForThisConfigurationStatement);
                    }
                }

                #endregion

                // End of dynamic keyword definition for this function...
                //############################################################
                //############################################################

                bool isMetaConfiguration = false;
                if (customAttributes != null)
                {
                    isMetaConfiguration = customAttributes.Any(
                         attribute => (attribute.TypeName.GetReflectionAttributeType() != null) &&
                                      (attribute.TypeName.GetReflectionAttributeType() == typeof(DscLocalConfigurationManagerAttribute)));
                }
                ScriptBlockExpressionAst bodyAst = configurationBodyScriptBlock as ScriptBlockExpressionAst;
                IScriptExtent configurationExtent = ExtentOf(startExtent, bodyAst);
                return new ConfigurationDefinitionAst(configurationExtent,
                    bodyAst,
                    isMetaConfiguration ? ConfigurationType.Meta : ConfigurationType.Resource,
                    configurationName)
                {
                    LCurlyToken = lCurly,
                    ConfigurationToken = configurationToken,
                    CustomAttributes = customAttributes,
                    DefinedKeywords = DynamicKeyword.GetKeyword()
                };
            }
            catch (Exception e)
            {
                CommandProcessorBase.CheckForSevereException(e);

                // In theory this should never happen so if it does, we'll report the actual exception rather than introducing a new message
                ReportError(configurationKeywordToken.Extent, () => "ConfigurationStatementToken: " + e);
                return null;
            }
            finally
            {
                // If we had to allocate a runspace for the parser, we'll free it now.
                if (localRunspace != null)
                {
                    // If a runspace was created for this operation, close it and reset the default runspace to null.
                    localRunspace.Close();
                    Runspaces.Runspace.DefaultRunspace = null;
                }

                if (topLevel)
                {
                    //
                    // Clear out all of the cached classes and keywords.
                    // They will need to be reloaded when the generated function is actually run.
                    //
                    Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.ClearCache();
                    System.Management.Automation.Language.DynamicKeyword.Reset();
                }

                // Finally resync the tokenizer at the current position 
                // this will flush any cached dynamic keyword tokens.
                var restorePoint = _tokenizer.GetRestorePoint();
                Resync(restorePoint);
            }
        }