This commit is contained in:
Bernie White 2020-05-25 16:55:28 -07:00 коммит произвёл GitHub
Родитель 5111f5062c
Коммит edb8deb881
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 493 добавлений и 37 удалений

3
.vscode/settings.json поставляемый
Просмотреть файл

@ -40,6 +40,7 @@
"deserialize",
"deserialized",
"deserializes",
"hashtable"
"hashtable",
"unencrypted"
]
}

Просмотреть файл

@ -2,6 +2,9 @@
## Unreleased
- Bug fixes:
- Fixed binding with ModuleConfig. [#468](https://github.com/Microsoft/PSRule/issues/468)
## v0.17.0
What's changed since v0.16.0:

Просмотреть файл

@ -0,0 +1,28 @@
---
resource: All resources
online version: https://github.com/Microsoft/PSRule/blob/master/docs/scenarios/rule-module/Enterprise.Rules/en/Org.Az.Resource.Tagging.md
---
# Use mandatory tags
## SYNOPSIS
Each resource must be tagged with mandatory tags.
## DESCRIPTION
Azure resources can be tagged with additional metadata.
Our enterprise standard requires that the following tags are used:
- Environment
- BusinessUnit
- Department
- CostCode
## RECOMMENDATION
Consider tagging Azure resource with mandatory tags.
## LINKS
- [Use tags to organize your Azure resources and management hierarchy](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources)

Просмотреть файл

@ -0,0 +1,33 @@
---
resource: Storage Account
online version: https://github.com/Microsoft/PSRule/blob/master/docs/scenarios/rule-module/Enterprise.Rules/en/Org.Az.Storage.UseHttps.md
---
# Enforce encrypted Storage connections
## SYNOPSIS
Storage accounts should only accept encrypted connections.
## DESCRIPTION
An Azure Storage Account is configured to allow unencrypted connections.
This does not indicate that unencrypted connections are being used.
Unencrypted communication to storage accounts could allow disclosure of information to an untrusted party.
Storage Accounts can be configured to require encrypted connections, by setting the _Secure transfer required_ option.
If _secure transfer required_ is not enabled (the default), unencrypted and encrypted connections are permitted.
When _secure transfer required_ is enabled, attempts to connect to storage using HTTP or unencrypted SMB connections are rejected.
## RECOMMENDATION
Storage accounts should only accept secure traffic.
Consider _setting secure transfer_ required if there is no requirement to access storage over unencrypted connections.
Also consider using Azure Policy to audit or enforce this configuration.
## LINKS
- [Require secure transfer in Azure Storage](https://docs.microsoft.com/en-au/azure/storage/common/storage-require-secure-transfer)
- [Sample policy for ensuring https traffic](https://docs.microsoft.com/en-au/azure/governance/policy/samples/ensure-https-stor-acct)

Просмотреть файл

@ -0,0 +1,15 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# Synopsis: Configure storage accounts to only accept encrypted traffic i.e. HTTPS/SMB
Rule 'Org.Az.Storage.UseHttps' -Type 'Microsoft.Storage/storageAccounts' -Tag @{ release = 'GA' } {
$Assert.HasFieldValue($TargetObject, 'Properties.supportsHttpsTrafficOnly', $True);
}
# Synopsis: Require mandatory tags
Rule 'Org.Az.Resource.Tagging' {
Exists 'tags.Environment'
Exists 'tags.BusinessUnit'
Exists 'tags.Department'
Exists 'tags.CostCode'
}

Просмотреть файл

@ -0,0 +1,326 @@
[
{
"Name": "storage",
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/storage",
"ResourceName": "storage",
"ResourceType": "Microsoft.Storage/storageAccounts",
"Kind": "Storage",
"ResourceGroupName": "test-rg",
"Location": "eastus2",
"SubscriptionId": "00000000-0000-0000-0000-000000000000",
"Tags": {
"role": "deployment",
"environment": "production"
},
"Properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": false,
"encryption": {
"services": {
"file": {
"enabled": true
},
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"primaryEndpoints": {
"blob": "https://storage.blob.core.windows.net/",
"queue": "https://storage.queue.core.windows.net/",
"table": "https://storage.table.core.windows.net/",
"file": "https://storage.file.core.windows.net/"
},
"primaryLocation": "eastus2",
"statusOfPrimary": "available"
},
"Sku": {
"name": "Standard_LRS",
"tier": "Standard"
}
},
{
"Name": "app-service-plan",
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Web/serverfarms/app-service-plan",
"ResourceName": "app-service-plan",
"ResourceType": "Microsoft.Web/serverfarms",
"Kind": "app",
"ResourceGroupName": "test-rg",
"Location": "East US 2",
"SubscriptionId": "00000000-0000-0000-0000-000000000000",
"Tags": {
"environment": "production",
"costCentre": "12345",
"businessUnit": "IT Operations"
},
"Properties": {
"name": "app-service-plan",
"workerSize": "Default",
"workerSizeId": 0,
"workerTierName": null,
"numberOfWorkers": 1,
"currentWorkerSize": "Default",
"currentWorkerSizeId": 0,
"currentNumberOfWorkers": 1,
"adminSiteName": null,
"hostingEnvironment": null,
"hostingEnvironmentProfile": null,
"maximumNumberOfWorkers": 10,
"planName": "VirtualDedicatedPlan",
"adminRuntimeSiteName": null,
"computeMode": "Dedicated",
"siteMode": null,
"geoRegion": "East US 2",
"perSiteScaling": false,
"maximumElasticWorkerCount": 0,
"numberOfSites": 2,
"hostingEnvironmentId": null,
"isSpot": false,
"spotExpirationTime": null,
"freeOfferExpirationTime": null,
"tags": {
"environment": "production"
},
"kind": "app",
"resourceGroup": "test-rg",
"reserved": false,
"isXenon": false,
"hyperV": false,
"targetWorkerCount": 0,
"targetWorkerSizeId": 0,
"webSiteId": null
},
"Sku": {
"name": "S1",
"tier": "Standard",
"size": "S1",
"family": "S",
"capacity": 1
}
},
{
"Name": "web-app",
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Web/sites/web-app",
"ResourceName": "web-app",
"ResourceType": "Microsoft.Web/sites",
"Kind": "app",
"ResourceGroupName": "test-rg",
"Location": "East US 2",
"SubscriptionId": "00000000-0000-0000-0000-000000000000",
"Tags": {
"businessUnit": "IT Operations"
},
"Properties": {
"name": "web-app",
"state": "Running",
"hostNames": [
"web-app.azurewebsites.net"
],
"repositorySiteName": "web-app",
"owner": null,
"usageState": "Normal",
"enabled": true,
"adminEnabled": true,
"enabledHostNames": [
"web-app.azurewebsites.net",
"web-app.scm.azurewebsites.net"
],
"siteProperties": {
"metadata": null,
"properties": [
{
"name": "LinuxFxVersion",
"value": ""
},
{
"name": "WindowsFxVersion",
"value": null
}
],
"appSettings": null
},
"availabilityState": "Normal",
"sslCertificates": null,
"csrs": [],
"cers": null,
"siteMode": null,
"hostNameSslStates": [
{
"name": "web-app.azurewebsites.net",
"sslState": "Disabled",
"ipBasedSslResult": null,
"virtualIP": null,
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"ipBasedSslState": "NotConfigured",
"hostType": "Standard"
},
{
"name": "web-app.scm.azurewebsites.net",
"sslState": "Disabled",
"ipBasedSslResult": null,
"virtualIP": null,
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"ipBasedSslState": "NotConfigured",
"hostType": "Repository"
}
],
"computeMode": null,
"serverFarm": null,
"serverFarmId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Web/serverfarms/app-service-plan",
"reserved": false,
"isXenon": false,
"hyperV": false,
"storageRecoveryDefaultState": "Running",
"contentAvailabilityState": "Normal",
"runtimeAvailabilityState": "Normal",
"siteConfig": null,
"trafficManagerHostNames": null,
"sku": "Standard",
"scmSiteAlsoStopped": false,
"targetSwapSlot": null,
"hostingEnvironment": null,
"hostingEnvironmentProfile": null,
"clientAffinityEnabled": false,
"clientCertEnabled": false,
"hostNamesDisabled": false,
"domainVerificationIdentifiers": null,
"kind": "app",
"containerSize": 0,
"dailyMemoryTimeQuota": 0,
"suspendedTill": null,
"siteDisabledReason": 0,
"functionExecutionUnitsCache": null,
"maxNumberOfWorkers": null,
"cloningInfo": null,
"hostingEnvironmentId": null,
"tags": null,
"resourceGroup": "test-rg",
"defaultHostName": "web-app.azurewebsites.net",
"slotSwapStatus": {
"sourceSlotName": "staging",
"destinationSlotName": "Production"
},
"httpsOnly": false
}
},
{
"Name": "web-app/staging",
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Web/sites/web-app/slots/staging",
"ResourceName": "web-app/staging",
"ResourceType": "Microsoft.Web/sites/slots",
"Kind": "app",
"ResourceGroupName": "test-rg",
"Location": "East US 2",
"SubscriptionId": "00000000-0000-0000-0000-000000000000",
"Tags": {
"businessUnit": "IT Operations"
},
"Properties": {
"name": "web-app(staging)",
"state": "Running",
"hostNames": [
"web-app-staging.azurewebsites.net"
],
"repositorySiteName": "web-app",
"owner": null,
"usageState": "Normal",
"enabled": true,
"adminEnabled": true,
"enabledHostNames": [
"web-app-staging.azurewebsites.net",
"web-app-staging.scm.azurewebsites.net"
],
"siteProperties": {
"metadata": null,
"properties": [
{
"name": "LinuxFxVersion",
"value": ""
},
{
"name": "WindowsFxVersion",
"value": null
}
],
"appSettings": null
},
"availabilityState": "Normal",
"sslCertificates": null,
"csrs": [],
"cers": null,
"siteMode": null,
"hostNameSslStates": [
{
"name": "web-app-staging.azurewebsites.net",
"sslState": "Disabled",
"ipBasedSslResult": null,
"virtualIP": null,
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"ipBasedSslState": "NotConfigured",
"hostType": "Standard"
},
{
"name": "web-app-staging.scm.azurewebsites.net",
"sslState": "Disabled",
"ipBasedSslResult": null,
"virtualIP": null,
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"ipBasedSslState": "NotConfigured",
"hostType": "Repository"
}
],
"computeMode": null,
"serverFarm": null,
"serverFarmId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Web/serverfarms/app-service-plan",
"reserved": false,
"isXenon": false,
"hyperV": false,
"storageRecoveryDefaultState": "Running",
"contentAvailabilityState": "Normal",
"runtimeAvailabilityState": "Normal",
"siteConfig": null,
"deploymentId": "web-app",
"trafficManagerHostNames": null,
"sku": "Standard",
"scmSiteAlsoStopped": false,
"targetSwapSlot": null,
"hostingEnvironment": null,
"hostingEnvironmentProfile": null,
"clientAffinityEnabled": false,
"clientCertEnabled": false,
"hostNamesDisabled": false,
"domainVerificationIdentifiers": null,
"kind": "app",
"containerSize": 0,
"dailyMemoryTimeQuota": 0,
"suspendedTill": null,
"siteDisabledReason": 0,
"functionExecutionUnitsCache": null,
"maxNumberOfWorkers": null,
"cloningInfo": null,
"hostingEnvironmentId": null,
"tags": null,
"resourceGroup": "test-rg",
"defaultHostName": "web-app-staging.azurewebsites.net",
"slotSwapStatus": {
"sourceSlotName": "staging",
"destinationSlotName": "Production"
},
"httpsOnly": true
}
}
]

Просмотреть файл

@ -101,9 +101,9 @@ For example:
- Enterprise.Rules/
- rules/
- NamingStandards.Rule.ps1
- ComplianceStandards.Rule.ps1
- Baseline.Rule.yaml
- Config.Rule.yaml
- Standards.Rule.ps1
- Enterprise.Rules.psd1
### File names
@ -111,7 +111,7 @@ For example:
For PSRule to find rules included in a module, rule file names must end with the `.Rule.ps1` suffix.
We recommend using the exact case `.Rule.ps1`.
This is because some file systems are case-sensitive.
For example, on Linux `NamingStandards.rule.ps1` would be ignored by PSRule.
For example, on Linux `Standards.rule.ps1` would be ignored by PSRule.
Similarly, when including baselines within a module use the `.Rule.yaml` suffix.
@ -122,7 +122,31 @@ To set a module configuration, define a `ModuleConfig` resource within an includ
A module configuration `.Rule.yaml` file must be distributed within the module directory structure.
PSRule only supports a single `ModuleConfig` resource.
Additional `ModuleConfig` resources are ignored.
The name of the `ModuleConfig` must match the name of the module.
Additional `ModuleConfig` resources or with an alternative name are ignored.
```yaml
---
# Synopsis: Example module configuration for Enterprise.Rules module
kind: ModuleConfig
metadata:
name: Enterprise.Rules
spec:
binding:
targetName:
- ResourceName
- FullName
- name
targetType:
- ResourceType
- type
- Extension
field:
resourceId: [ 'ResourceId' ]
subscriptionId: [ 'SubscriptionId' ]
resourceGroupName: [ 'ResourceGroupName' ]
```
The following options are allowed within a `ModuleConfig`:
@ -156,20 +180,20 @@ For example:
- Enterprise.Rules/
- en/
- Metadata.Name.md
- Org.Az.Storage.UseHttps.md
- Org.Az.Resource.Tagging.md
- en-US/
- Metadata.Name.md
- Org.Az.Storage.UseHttps.md
- fr-FR/
- Metadata.Name.md
- Org.Az.Storage.UseHttps.md
- rules/
- NamingStandards.Rule.ps1
- ComplianceStandards.Rule.ps1
- Baseline.Rule.yaml
- ModuleConfig.Rule.yaml
- Config.Rule.yaml
- Standards.Rule.ps1
- Enterprise.Rules.psd1
## More information
- [Enterprise.Rules.psd1](Enterprise.Rules/Enterprise.Rules.psd1) - An example module manifest.
- [Baseline.Rule.yaml](Enterprise.Rules/rules/Baseline.Rule.yaml) - An example module manifest.
- [ModuleConfig.Rule.yaml](Enterprise.Rules/rules/ModuleConfig.Rule.yaml) - An example module manifest.
- [Baseline.Rule.yaml](Enterprise.Rules/rules/Baseline.Rule.yaml) - An example baseline.
- [Config.Rule.yaml](Enterprise.Rules/rules/Config.Rule.yaml) - An example module configuration.

Просмотреть файл

@ -41,12 +41,18 @@ namespace PSRule.Host
return builder.Build();
}
/// <summary>
/// Read YAML objects and return baselines.
/// </summary>
public static IEnumerable<Baseline> GetBaseline(Source[] source, RunspaceContext context)
{
return ToBaseline(ReadYamlObjects(source, context), context);
}
public static IEnumerable<ModuleConfig> GetConfig(Source[] source, RunspaceContext context)
/// <summary>
/// Read YAML objects and return module configurations.
/// </summary>
public static IEnumerable<ModuleConfig> GetModuleConfig(Source[] source, RunspaceContext context)
{
return ToModuleConfig(ReadYamlObjects(source, context), context);
}
@ -208,10 +214,6 @@ namespace PSRule.Host
result.Add(item.Block);
}
if (result.Count == 0)
return null;
return result.ToArray();
}
}
}
@ -222,7 +224,7 @@ namespace PSRule.Host
context.Pipeline.ExecutionScope = ExecutionScope.None;
context.ExitSourceScope();
}
return result;
return result.Count == 0 ? Array.Empty<ILanguageBlock>() : result.ToArray();
}
public static void InvokeRuleBlock(RunspaceContext context, RuleBlock ruleBlock, RuleRecord ruleRecord)

Просмотреть файл

@ -269,7 +269,7 @@ namespace PSRule.Pipeline
internal void Add(BaselineScope scope)
{
if (scope.Type == ScopeType.Module)
if (scope.Type == ScopeType.Module && !string.IsNullOrEmpty(scope.ModuleName))
_ModuleBaselineScope.Add(scope.ModuleName, scope);
else if (scope.Type == ScopeType.Explicit)
_Explicit = scope;
@ -281,7 +281,7 @@ namespace PSRule.Pipeline
internal void Add(ConfigScope scope)
{
if (scope.Type == ScopeType.Module)
if (scope.Type == ScopeType.Module && !string.IsNullOrEmpty(scope.ModuleName))
_ModuleConfigScope.Add(scope.ModuleName, scope);
else if (scope.Type == ScopeType.Workspace)
_WorkspaceConfig = scope;

Просмотреть файл

@ -18,7 +18,7 @@ namespace PSRule
public void ReadModuleConfig()
{
var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, new OptionContext(), null), null);
var configuration = HostHelper.GetConfig(GetSource(), context).ToArray();
var configuration = HostHelper.GetModuleConfig(GetSource(), context).ToArray();
Assert.NotNull(configuration);
Assert.Equal("Configuration1", configuration[0].Name);
}

Просмотреть файл

@ -169,6 +169,28 @@ Describe 'Scenarios -- kubernetes-resources' -Tag 'EndToEnd','kubernetes-resourc
}
}
Describe 'Scenarios -- rule-module' -Tag 'EndToEnd', 'rule-module' {
$scenarioPath = Join-Path -Path $rootPath -ChildPath docs/scenarios/rule-module;
$inputPath = Join-Path -Path $scenarioPath -ChildPath 'resources.json';
Context 'Invoke-PSRule' {
Import-Module (Join-Path -Path $scenarioPath -ChildPath 'Enterprise.Rules') -Force;
$result = @(Invoke-PSRule -InputPath $inputPath -Module 'Enterprise.Rules' -WarningAction SilentlyContinue);
It 'Binds fields' {
$result.TargetName | Should -BeIn 'storage', 'app-service-plan', 'web-app', 'web-app/staging';
$result.TargetType | Should -BeIn @(
'Microsoft.Storage/storageAccounts'
'Microsoft.Web/serverfarms'
'Microsoft.Web/sites'
'Microsoft.Web/sites/slots'
)
$result.Field.SubscriptionId | Should -Be '00000000-0000-0000-0000-000000000000';
$result.Field.ResourceGroupName | Should -Be 'test-rg';
}
}
}
Describe 'Scenarios -- validation-pipeline' -Tag 'EndToEnd', 'validation-pipeline' {
$scenarioPath = Join-Path -Path $rootPath -ChildPath docs/scenarios/validation-pipeline;
$sourcePath = Join-Path -Path $rootPath -ChildPath src/PSRule;

Просмотреть файл

@ -45,9 +45,11 @@ Describe 'PSRule' -Tag 'PowerShellGallery' {
$filteredCommands | Should -Not -BeNullOrEmpty;
$expected = @(
'Invoke-PSRule'
'Test-PSRuleTarget'
'Assert-PSRule'
'Get-PSRule'
'Get-PSRuleHelp'
'Test-PSRuleTarget'
'Get-PSRuleBaseline'
'New-PSRuleOption'
'Set-PSRuleOption'
'Rule'

Просмотреть файл

@ -1,18 +1,4 @@
---
kind: ModuleConfig
metadata:
name: TestModule4
spec:
configuration:
ruleConfig3: 'TestConfig3'
output:
culture:
- 'en-US'
binding:
field:
AlternativeType: [ 'Kind' ]
---
kind: Baseline
metadata:

Просмотреть файл

@ -0,0 +1,14 @@
---
kind: ModuleConfig
metadata:
name: TestModule4
spec:
configuration:
ruleConfig3: 'TestConfig3'
output:
culture:
- 'en-US'
binding:
field:
AlternativeType: [ 'Kind' ]