Framework/Core/PolicySetup/PolicySetup.ps1

using namespace System.Management.Automation
using namespace Microsoft.Azure.Commands.Management.Storage.Models
using namespace Microsoft.WindowsAzure.Storage.Blob
using namespace Microsoft.WindowsAzure.Commands.Common.Storage.ResourceModel
Set-StrictMode -Version Latest

class PolicySetup: CommandBase
{
    [StorageHelper] $StorageAccountInstance;
    [AppInsightHelper] $AppInsightInstance;
    [string] $OrgName;
    [string] $DepartmentName;
    [string] $OrgFullName;

    hidden [string] $PolicyUrl;
    hidden [string] $InstallerUrl;
    hidden [string] $InstallerFileName;
    hidden [string] $Version = "2.1711.0";

    hidden [string] $ConfigContainerName = "policies";
    hidden [string] $InstallerContainerName = "installer";

    hidden [string] $FolderPath;
    hidden [string] $ConfigFolderPath;
    hidden [string] $InstallerFolderPath;
    hidden [string] $RunbookFolderPath;

    hidden [string] $InstallerFile;

    hidden [string] $IWRCommand;

    #PolicySetup([string] $subscriptionId, [InvocationInfo] $invocationContext, [string] $orgName, [string] $resourceGroupLocation):
    # Base($subscriptionId, $invocationContext)
    #{
    # $this.CreateInstance($subscriptionId, $orgName, "", $resourceGroupLocation);
    #}

    PolicySetup([string] $subscriptionId, [InvocationInfo] $invocationContext, [string] $orgName, [string] $departmentName, [string] $resourceGroupLocation, [string] $localPolicyFolderPath):
        Base($subscriptionId, $invocationContext)
    {
        $this.CreateInstance($subscriptionId, $orgName, $departmentName, $resourceGroupLocation, $localPolicyFolderPath);
    }

    [void] CreateInstance([string] $subscriptionId, [string] $orgName, [string] $departmentName, [string] $resourceGroupLocation, [string] $localPolicyFolderPath)
    {
        if([string]::IsNullOrWhiteSpace($orgName))
        {
            throw ([SuppressedException]::new(("The argument 'orgName' is null or empty"), [SuppressedExceptionType]::NullArgument))
        }
        $this.OrgName = $orgName;
        $this.DepartmentName = $departmentName;
        $this.OrgFullName = $orgName;

        if(-not [string]::IsNullOrWhiteSpace($departmentName))
        {
            $this.OrgFullName = $this.OrgFullName + "-" + $departmentName;
        }

        $prefix = [Constants]::AzSdkModuleName + "-" + $this.OrgFullName;

        $candidateStorageAccountName = $prefix.Replace("-", "").ToLower() + "sa";
        [int] $availableLength = 24 - ([Constants]::AzSdkModuleName.Length + 2)
        $storageRegEx = [regex]"^[a-z0-9]{3,24}$"
        if(-not $storageRegEx.IsMatch($candidateStorageAccountName))
        {
            throw ([SuppressedException]::new("Only alphanumeric characters are supported in OrgName and DeptName params. Total length (OrgName + DeptName) should be less than $availableLength characters ", [SuppressedExceptionType]::InvalidArgument))
        }

        $this.FolderPath = [System.Environment]::GetFolderPath("Desktop") + "\" + $prefix + "-Policy\";
        if(-not [string]::IsNullOrWhiteSpace($localPolicyFolderPath))
        {
            try
            {
                if (-not $localPolicyFolderPath.EndsWith("\"))
                {
                    $localPolicyFolderPath += "\";
                }

                $localPolicyFolderPath += $prefix + "-Policy\";

                if (-not (Test-Path $localPolicyFolderPath))
                {
                    mkdir -Path $localPolicyFolderPath -ErrorAction Stop | Out-Null
                }

                Copy-Item ($PSScriptRoot + "\README.txt") ($localPolicyFolderPath + "README.txt") -Force
                #Out-File -InputObject "AzSDK folder check" -Force -FilePath $testFilePath -Encoding utf8
                #Remove-Item -Path $testFilePath -Force
                $this.FolderPath = $localPolicyFolderPath;
            }
            catch
            {
                throw ([SuppressedException]::new("Not able to access/modify the folder [$localPolicyFolderPath].`r`n$($_.ToString())", [SuppressedExceptionType]::InvalidOperation))
            }
        }

        $this.StorageAccountInstance = [StorageHelper]::new($subscriptionId, $prefix + "-RG" , $resourceGroupLocation, $prefix.Replace("-", "").ToLower() + "sa");
        $this.AppInsightInstance = [AppInsightHelper]::new($subscriptionId, $prefix + "-RG" , $resourceGroupLocation, $prefix + "-AppInsight");

        $this.ConfigFolderPath = $this.FolderPath + "Config\";
        $this.InstallerFolderPath = $this.FolderPath + "Installer\";
        $this.RunbookFolderPath = $this.FolderPath + "CA-Runbook\";
        
        $this.InstallerFileName = [Constants]::AzSdkModuleName + "-EasyInstaller.ps1";
        $this.InstallerFile = $this.InstallerFolderPath + $this.InstallerFileName;

        # setup base version
        $azsdkConfig = [ConfigurationManager]::GetAzSdkConfigData();
        if([Helpers]::CheckMember($azsdkConfig, "ConfigSchemaBaseVersion") -and (-not [string]::IsNullOrWhiteSpace($azsdkConfig.ConfigSchemaBaseVersion)))
        {
            $this.Version = $azsdkConfig.ConfigSchemaBaseVersion;
        }
    }

    [void] ModifyConfigs()
    {
        # Modify config files and save to temp location
        $metadataFileNames = @();

        if(-not [string]::IsNullOrWhiteSpace($this.InstallerUrl))
        {
            $this.IWRCommand = "iwr '$($this.InstallerUrl)' -UseBasicParsing | iex";
        }

        if((Get-ChildItem $this.ConfigFolderPath -Recurse -Force | Where-Object { $_.Name -eq "AzSdk.json" } | Measure-Object).Count -eq 0)
        {
            $azsdkOverride = [ConfigOverride]::new("AzSdk.json");
            $azsdkOverride.UpdatePropertyValue("PolicyMessage", "Running $([Constants]::AzSdkModuleName) cmdlet using $($this.OrgFullName) policy...");
            if(-not [string]::IsNullOrWhiteSpace($this.IWRCommand))
            {
                $azsdkOverride.UpdatePropertyValue("InstallationCommand", $this.IWRCommand);
            }

            if($this.AppInsightInstance -and $this.AppInsightInstance.AppInsightInstance -and $this.AppInsightInstance.AppInsightInstance.Properties)
            {
                $azsdkOverride.UpdatePropertyValue("ControlTelemetryKey", $this.AppInsightInstance.AppInsightInstance.Properties.InstrumentationKey);
                $azsdkOverride.UpdatePropertyValue("EnableControlTelemetry", "true");
            }
            $azsdkOverride.WriteToFolder($this.ConfigFolderPath);
        }

        # Dynamically get list of files available in folder
        $metadataFileNames += Get-ChildItem $this.ConfigFolderPath -Recurse -Force |
                                Where-Object { $_.mode -match "-a---" -and $_.Name -ne "ServerConfigMetadata.json" } |
                                Select-Object -Property Name | Select-Object -ExpandProperty Name |
                                Select-Object @{ Label="Name"; Expression={ $_ } };

        $metadataOverride = [ConfigOverride]::new("ServerConfigMetadata.json");
        $metadataOverride.UpdatePropertyValue("OnlinePolicyList", $metadataFileNames);
        $metadataOverride.WriteToFolder($this.ConfigFolderPath);
    }

    [void] ModifyInstaller()
    {
        if(-not [string]::IsNullOrWhiteSpace($this.PolicyUrl))
        {
            # write SAS token url to installer file
            $folderName = [System.IO.Path]::GetDirectoryName($this.InstallerFile);

            # Check for environment specific installer file
            $fileName = $PSScriptRoot + "\" + [Constants]::AzSdkModuleName +"-EasyInstaller.ps1";
            if(-not (Test-Path -Path $fileName))
            {
                $fileName = $PSScriptRoot + "\EasyInstaller.ps1";
            }
            $fileContent = Get-Content -Path $fileName;
            $fileContent = $fileContent.Replace("#PolicyUrl#", $this.PolicyUrl);
            $fileContent = $fileContent.Replace("#ModuleName#", [Constants]::AzSdkModuleName);
            $fileContent = $fileContent.Replace("#OrgName#", $this.OrgFullName);

            if (-not (Test-Path $folderName))
            {
                mkdir -Path $folderName -ErrorAction Stop | Out-Null
            }

            if (-not $folderName.EndsWith("\"))
            {
                $folderName += "\";
            }

            Out-File -InputObject $fileContent -Force -FilePath $this.InstallerFile -Encoding utf8
            #Set-Content -Value $fileContent -Path $this.InstallerFile -Force
        }
        else
        {
            throw ([SuppressedException]::new("Not able to create installer file.", [SuppressedExceptionType]::Generic))
        }
    }

    [void] CopyRunbook()
    {
        # Quick fix: Temporary code added to make the policies run for non-CSE tenants. Suggested by Byna.
        try
        {
            if (-not (Test-Path $this.RunbookFolderPath))
            {
                mkdir -Path $this.RunbookFolderPath -ErrorAction Stop | Out-Null
            }
            
            if((Get-ChildItem $this.RunbookFolderPath -Force | Where-Object { $_.Name -eq "RunbookScanAgent.ps1" } | Measure-Object).Count -eq 0)
            {
                $caFilePath = (Get-Item -Path $PSScriptRoot).Parent.Parent.FullName + "\Configurations\ContinuousAssurance\RunbookScanAgent.ps1"
                Copy-Item ($caFilePath) ($this.RunbookFolderPath + "RunbookScanAgent.ps1")
            }

            $allCAFiles = @();
            $allCAFiles += Get-ChildItem $this.RunbookFolderPath -Force | Where-Object { $_.mode -match "-a---" }
            if($allCAFiles.Count -ne 0)
            {
                $this.StorageAccountInstance.UploadFilesToBlob($this.ConfigContainerName, "0.0.0", $allCAFiles);
            }
        }
        catch
        {
            $this.CommandError($_);
        }        
    }


    [MessageData[]] InstallPolicy()
    {
        $this.AppInsightInstance.CreateAppInsightIfNotExists();
        $container = $this.StorageAccountInstance.CreateStorageContainerIfNotExists($this.InstallerContainerName, [BlobContainerPublicAccessType]::Blob);
        if($container -and $container.CloudBlobContainer)
        {
            $this.InstallerUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/" + $this.InstallerFileName;
        }
        $this.ModifyConfigs();

        $allFiles = @();
        $allFiles += Get-ChildItem $this.ConfigFolderPath -Recurse -Force | Where-Object { $_.mode -match "-a---" } #| Select-Object -Property FullName | Select-Object -ExpandProperty FullName;
        if($allFiles.Count -ne 0)
        {
            $this.StorageAccountInstance.UploadFilesToBlob($this.ConfigContainerName, $this.Version, $allFiles);
        }
        else
        {
            #throw ([SuppressedException]::new("No files found under folder [$($this.ConfigFolderPath)]", [SuppressedExceptionType]::InvalidOperation))
        }

        $container = $this.StorageAccountInstance.CreateStorageContainerIfNotExists($this.ConfigContainerName);
        if($container -and $container.CloudBlobContainer)
        {
            $this.PolicyUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/```$(```$Version)/```$(```$FileName)" + $this.StorageAccountInstance.GenerateSASToken($this.ConfigContainerName);
        }
        $this.ModifyInstaller();

        $this.StorageAccountInstance.UploadFilesToBlob($this.InstallerContainerName, "", (Get-ChildItem -Path $this.InstallerFile));

        $this.CopyRunbook();

        $this.PublishCustomMessage(" `r`nThe setup has been completed and policies have been copied to [$($this.FolderPath)].`r`nRun the command below to install Organization specific version.`r`n$($this.IWRCommand)", [MessageType]::Update);
        $this.PublishCustomMessage(" `r`nNote: This is a basic setup and uses a public access blob for storing your org's installer. Once you have richer org policies, consider using a location/end-point protected by your tenant authentication.", [MessageType]::Warning);


        # Removing cleanup step
        #$this.CleanupTempFolders();
        return @();
    }

    [void] CleanupTempFolders()
    {
        Remove-Item -Path $this.ConfigFolderPath -Force -Recurse -ErrorAction Ignore
        Remove-Item -Path $this.InstallerFolderPath -Force -Recurse -ErrorAction Ignore
    }
}