update
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.All-General'
|
||||
|
||||
variables:
|
||||
major: 1
|
||||
minor: 0
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.All-General.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.All-General.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.All-General'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.All-General).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.All-General</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>QoL functions that don't fit into another module</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,11 @@
|
||||
@{
|
||||
RootModule = 'ITD.All-General.psm1'
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
GUID = '9c9219d5-d7be-49e5-8448-49c50df94eda'
|
||||
Author = 'Zack Meier'
|
||||
CompanyName = 'State of North Dakota'
|
||||
Description = "QoL functions that don't fit into another module"
|
||||
PowerShellVersion = '5.1'
|
||||
CompatiblePSEditions = 'Desktop', 'Core'
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#Get public and private function definition files.
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
|
||||
|
||||
#Dot source the files
|
||||
Foreach($import in @($Public + $Private))
|
||||
{
|
||||
Try
|
||||
{
|
||||
. $import.fullname
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Here I might...
|
||||
# Read in or create an initial config file and variable
|
||||
# Export Public functions ($Public.BaseName) for WIP modules
|
||||
# Set variables visible to the module and its functions only
|
||||
|
||||
Export-ModuleMember -Function $Public.Basename
|
||||
@@ -0,0 +1,32 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Short description
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
.EXAMPLE
|
||||
$servers=@"
|
||||
server1.xyz.com
|
||||
servers2.xyz.com
|
||||
@"
|
||||
|
||||
$servers = ConvertTo-Array -MultiLineString $servers
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
#>
|
||||
function ConvertTo-Array {
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[string]
|
||||
$MultiLineString
|
||||
)
|
||||
|
||||
Begin {
|
||||
}
|
||||
Process {
|
||||
$result = @($MultiLineString -split '[\r\n]+')
|
||||
}
|
||||
End {
|
||||
return $result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
Test-MyTestFunction -Verbose
|
||||
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
|
||||
#>
|
||||
|
||||
function Get-SslCertificate {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[string]$DNSName
|
||||
)
|
||||
|
||||
process {
|
||||
Write-Verbose "Checking certificate for $DNSName"
|
||||
|
||||
$tcp = [Net.Sockets.TcpClient]::new($DNSName, 443)
|
||||
$ssl = [Net.Security.SslStream]::new(
|
||||
$tcp.GetStream(),
|
||||
$false,
|
||||
{ $true }
|
||||
)
|
||||
|
||||
$ssl.AuthenticateAsClient($DNSName)
|
||||
|
||||
$cert = [Security.Cryptography.X509Certificates.X509Certificate2]::new(
|
||||
$ssl.RemoteCertificate
|
||||
)
|
||||
|
||||
$ssl.Dispose()
|
||||
$tcp.Dispose()
|
||||
|
||||
$cert
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Verify AD credentials are valid
|
||||
.DESCRIPTION
|
||||
Verify AD credentials are valid ##
|
||||
.EXAMPLE
|
||||
Test-ADCredential -Credential <PSCredential>
|
||||
#>
|
||||
function Test-ADCredential {
|
||||
[CmdletBinding()]#
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin {
|
||||
|
||||
}
|
||||
Process {
|
||||
If ($Credential -eq $null) {
|
||||
Write-Warning "Credentials empty"
|
||||
$status = $true
|
||||
}
|
||||
Else {
|
||||
$username = $Credential.username
|
||||
$password = $Credential.GetNetworkCredential().password
|
||||
$CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
|
||||
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
|
||||
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
|
||||
|
||||
#($ValidateCredential = ) | Out-Null
|
||||
|
||||
If ($DS.ValidateCredentials($UserName, $Password) -eq $false) {
|
||||
$password = $null
|
||||
Write-Error "Invalid credentials or locked account."
|
||||
$status = $false
|
||||
}
|
||||
Else {
|
||||
$status = $true
|
||||
}
|
||||
|
||||
$password = $null
|
||||
}
|
||||
}
|
||||
End {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Removes old versions of ITD modules
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
.EXAMPLE
|
||||
Example of how to use this cmdlet
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
.INPUTS
|
||||
Inputs to this cmdlet (if any)
|
||||
.OUTPUTS
|
||||
Output from this cmdlet (if any)
|
||||
.NOTES
|
||||
General notes
|
||||
.COMPONENT
|
||||
The component this cmdlet belongs to
|
||||
.ROLE
|
||||
The role this cmdlet belongs to
|
||||
.FUNCTIONALITY
|
||||
The functionality that best describes this cmdlet
|
||||
#>
|
||||
function Uninstall-ITDModuleOldVersion {
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
)
|
||||
|
||||
begin {
|
||||
}
|
||||
|
||||
process {
|
||||
$InstalledModules = Get-InstalledModule -Name ITD.*
|
||||
|
||||
try {
|
||||
$InstalledModules | ForEach-Object {
|
||||
$CurrentVersion = $_.Version
|
||||
Get-InstalledModule -Name $_.Name -AllVersions | Where-Object -Property Version -LT -Value $CurrentVersion
|
||||
} | Uninstall-Module -Verbose
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
Test-MyTestFunction -Verbose
|
||||
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
|
||||
#>
|
||||
|
||||
function Update-ITDModule {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
$InstalledModules = Get-InstalledModule -Name ITD.*
|
||||
Update-Module -Name $InstalledModules.Name
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Introduction
|
||||
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
|
||||
|
||||
# Getting Started
|
||||
TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
|
||||
1. Installation process
|
||||
2. Software dependencies
|
||||
3. Latest releases
|
||||
4. API references
|
||||
|
||||
# Build and Test
|
||||
TODO: Describe and show how to build your code and run the tests.
|
||||
|
||||
# Contribute
|
||||
TODO: Explain how other users and developers can contribute to make your code better.
|
||||
|
||||
If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
|
||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
||||
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.ITD-WindowsServer.FileManagement'
|
||||
|
||||
variables:
|
||||
major: 0
|
||||
minor: 7
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.ITD-WindowsServer.FileManagement.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.ITD-WindowsServer.FileManagement.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.ITD-WindowsServer.FileManagement'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.ITD-WindowsServer.FileManagement).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"ComputerName": "itdxyz.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"emailA@nd.gov",
|
||||
"emailB@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs\\LogFiles",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 90,
|
||||
"Recursive": true
|
||||
},
|
||||
{
|
||||
"Path": "C:\\temp",
|
||||
"Extension": "txt",
|
||||
"DaysToKeep": 30,
|
||||
"Recursive": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"ComputerName": "itddohslimst2.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"zmeier@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 120,
|
||||
"Recursive": true
|
||||
},
|
||||
{
|
||||
"Path": "C:\\Program Files\\STARLIMS\\STARLIMStst\\Log",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 120,
|
||||
"Recursive": true
|
||||
},
|
||||
{
|
||||
"Path": "E:\\Program Files\\STARLIMS\\STARLIMSdev\\Log",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 120,
|
||||
"Recursive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ComputerName": "itdernappt01.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"zmeier@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 90,
|
||||
"Recursive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ComputerName": "itdernappu01.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"zmeier@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 90,
|
||||
"Recursive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"ComputerName": "itdscmt1.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"zmeier@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 90,
|
||||
"Recursive": true
|
||||
},
|
||||
{
|
||||
"Path": "C:\\temp",
|
||||
"Extension": "txt",
|
||||
"DaysToKeep": 30,
|
||||
"Recursive": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ComputerName": "itdvmvc1script.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"zmeier@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs\\LogFiles",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 90,
|
||||
"Recursive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ComputerName": "itdwinautot1.nd.gov",
|
||||
"NotifyEmail": [
|
||||
"zmeier@nd.gov"
|
||||
],
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\temp",
|
||||
"Extension": "txt",
|
||||
"DaysToKeep": 30,
|
||||
"Recursive": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.ITD-WindowsServer.FileManagement</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>Functions for Windows Server file management</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
#
|
||||
# Module manifest for module 'ITD.ITD-WindowsServer.FileManagement'
|
||||
#
|
||||
# Generated by: zmeier
|
||||
#
|
||||
# Generated on: 6/14/2022
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'ITD.ITD-WindowsServer.FileManagement.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
|
||||
# Supported PSEditions
|
||||
CompatiblePSEditions = 'Desktop', 'Core'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '85e7676b-f0e3-4908-aafa-25c9606ca8b7'
|
||||
|
||||
# Author of this module
|
||||
Author = 'zmeier'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'State of North Dakota'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) zmeier. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'Functions for Windows Server file management'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#Get public and private function definition files.
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
|
||||
|
||||
#Dot source the files
|
||||
Foreach($import in @($Public + $Private))
|
||||
{
|
||||
Try
|
||||
{
|
||||
. $import.fullname
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Here I might...
|
||||
# Read in or create an initial config file and variable
|
||||
# Export Public functions ($Public.BaseName) for WIP modules
|
||||
# Set variables visible to the module and its functions only
|
||||
|
||||
Export-ModuleMember -Function $Public.Basename
|
||||
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"ComputerName": "itdscmt1.nd.gov",
|
||||
"NotifyEmail": "zmeier@nd.gov",
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\inetpub\\logs\\LogFiles",
|
||||
"DaysToKeep": 120,
|
||||
"Extension": "log",
|
||||
"Recursive": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ComputerName": "itdzmtest555.nd.gov",
|
||||
"NotifyEmail": "zmeier@nd.gov",
|
||||
"Directory": [
|
||||
{
|
||||
"Path": "C:\\windows\\temp",
|
||||
"DaysToKeep": 99,
|
||||
"Extension": "log",
|
||||
"Recursive": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
- ComputerName: itdscmt1.nd.gov
|
||||
Directory:
|
||||
- Path: C:\windows\temp
|
||||
DaysToKeep: 30
|
||||
Extension: txt
|
||||
Recursive: false
|
||||
- ComputerName: itdsccmp2.nd.gov
|
||||
Directory:
|
||||
- Path: C:\windows\temp
|
||||
DaysToKeep: 15
|
||||
Extension: txt
|
||||
Recursive: false
|
||||
- Path: C:\inetpub\logs
|
||||
DaysToKeep: 120
|
||||
Extension: txt
|
||||
Recursive: false
|
||||
- ComputerName: itdzmtest555.nd.gov
|
||||
Directory:
|
||||
- Path: "C:\windows\temp",
|
||||
DaysToKeep": 99,
|
||||
Extension": "log",
|
||||
Recursive": false
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"Path": "C:\\temp",
|
||||
"Extension": "log",
|
||||
"DaysToKeep": 30,
|
||||
"Recursive": true
|
||||
},
|
||||
{
|
||||
"Path": "C:\\temp2",
|
||||
"Extension": "txt",
|
||||
"DaysToKeep": 45,
|
||||
"Recursive": false
|
||||
}
|
||||
]
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Script will discover all files that are considered "expired".
|
||||
.DESCRIPTION
|
||||
Script will discover all files that are considered "expired" based on the parameters in the ./Helpers/ITDExpiredFiles.json files in the ITD-WindowsServer module
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
|
||||
.EXAMPLE
|
||||
Get-ITDExpiredFiles
|
||||
.EXAMPLE
|
||||
Get-ITDExpiredFiles -Credential $PrvCred
|
||||
.EXAMPLE
|
||||
Get-ITDExpiredFiles -ComputerName itdxyz.nd.gov -Credential $PrvCred
|
||||
#>
|
||||
|
||||
function Get-ITDExpiredFiles {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
Begin {
|
||||
Write-Verbose -Message "Load json files into memory"
|
||||
$JsonFiles = Get-ChildItem -Path "$PSScriptRoot\..\Helpers\*.json" | Where-Object Name -NE ^template.json
|
||||
$MachineInfo = ForEach ($file in $JsonFiles) {
|
||||
Get-Content -Path $file.FullName | ConvertFrom-Json
|
||||
}
|
||||
|
||||
If ($PSBoundParameters.ContainsKey('ComputerName')) {
|
||||
Write-Verbose -Message "ComputerName found"
|
||||
$MachineInfo = $MachineInfo | Where-Object ComputerName -EQ $ComputerName
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
Write-Verbose -Message "Prep discovery function"
|
||||
$GetExpiredFilesFunc = {
|
||||
Write-Verbose -Message ($env:COMPUTERNAME + " " + $args[0] + " " + $args[1] + " " + $args[2])
|
||||
|
||||
$GetChildItemParams = @{
|
||||
Path = $args[0];
|
||||
Filter = $args[1];
|
||||
Recurse = $args[2];
|
||||
}
|
||||
|
||||
$FilesFound = (Get-ChildItem @GetChildItemParams | Where-Object LastWriteTime -LT ((Get-Date).AddDays(-$args[3])))
|
||||
Write-Output $FilesFound
|
||||
}
|
||||
|
||||
$GetITDExpiredFilesResult = [System.Collections.ArrayList]@()
|
||||
ForEach ($Server in $MachineInfo) {
|
||||
Write-Verbose -Message ("Start " + $Server.ComputerName)
|
||||
|
||||
Write-Verbose -Message "Ping test before any Invoke-Command"
|
||||
If ((Test-NetConnection -ComputerName $Server.ComputerName).PingSucceeded) {
|
||||
ForEach ($Directory in $Server.Directory) {
|
||||
Write-Verbose -Message ("Start " + $server.ComputerName + " " + $Directory.Path)
|
||||
$FilesFoundOnServer = $null
|
||||
$InvokeCommandParams = $null
|
||||
$InvokeResult = $null
|
||||
|
||||
$InvokeCommandParams = @{
|
||||
ComputerName = $Server.ComputerName;
|
||||
Credential = $Credential;
|
||||
ScriptBlock = $GetExpiredFilesFunc;
|
||||
ArgumentList = @($Directory.Path, ("*" + $Directory.Extension), $Directory.Recursive, $Directory.DaysToKeep);
|
||||
}
|
||||
$FilesFoundOnServer = Invoke-Command @InvokeCommandParams
|
||||
Write-Output $FilesFoundOnServer
|
||||
#$null = $GetITDExpiredFilesResult.Add($FilesFoundOnServer)
|
||||
Write-Verbose -Message ("End " + $server.ComputerName + " " + $Directory.Path)
|
||||
}
|
||||
}
|
||||
Else {
|
||||
Write-Error -Message ($Server.ComputerName + " ping test failed, generate ticket someday.")
|
||||
}
|
||||
}
|
||||
Write-Verbose -Message ("End " + $server.ComputerName)
|
||||
}
|
||||
End {
|
||||
#Write-Output $GetITDExpiredFilesResult
|
||||
Write-Verbose -Message "End Get-ITDExpiredFiles"
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
function Remove-ITDExpiredFiles {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[switch]
|
||||
$WhatIf,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
Begin {
|
||||
Write-Verbose -Message "Start Remove-ITDExpiredFiles"
|
||||
}
|
||||
Process {
|
||||
$FilesRemovedSuccess = @()
|
||||
$FilesRemovedFailure = @()
|
||||
$GetITDExpiredFilesAutoParams += @{}
|
||||
|
||||
If ($PSBoundParameters.ContainsKey('ComputerName')) {
|
||||
Write-Verbose -Message "ComputerName parameter found"
|
||||
$GetITDExpiredFilesParams += @{
|
||||
ComputerName = $ComputerName;
|
||||
}
|
||||
}
|
||||
|
||||
If ($PSBoundParameters.ContainsKey('Credential')) {
|
||||
Write-Verbose -Message "Credential parameter found"
|
||||
$GetITDExpiredFilesParams += @{
|
||||
Credential = $Credential;
|
||||
}
|
||||
}
|
||||
$FilesToRemove = Get-ITDExpiredFiles @GetITDExpiredFilesParams
|
||||
Write-Verbose -Message ("Found " + $FilesToRemove.count + " expired files to remove")
|
||||
|
||||
ForEach ($File in $FilesToRemove) {
|
||||
Write-Verbose -Message ("Start~" + $File.PSComputerName + "~" + $File.FullName )
|
||||
$InvokeCommandParams = @{
|
||||
ComputerName = $File.PSComputerName;
|
||||
Credential = $Credential;
|
||||
ErrorAction = 'Stop';
|
||||
ArgumentList = @($File.FullName);
|
||||
ScriptBlock = { Get-Item -Path $args[0] | Remove-Item }
|
||||
}
|
||||
|
||||
switch ($WhatIf) {
|
||||
$true {
|
||||
try {
|
||||
Write-Verbose -Message ("Process~" + $File.PSComputerName + "~" + $File.FullName + " removed")
|
||||
Write-Host -Message ($Server.ComputerName + " -- " + 'What if: Performing the operation "Remove File" on target ' + $File.FullName)
|
||||
# log success
|
||||
$FilesRemovedSuccess += [PSCustomObject]@{
|
||||
ComputerName = $File.PSComputerName;
|
||||
Name = $File.Fullname;
|
||||
Timestamp = (Get-Date).tostring("yyyy/MM/dd HH:mm:ss")
|
||||
}
|
||||
Write-Output $File
|
||||
}
|
||||
catch {
|
||||
Write-Verbose -Message ("Process~" + $File.PSComputerName + "~" + $File.FullName + " failure")
|
||||
# log failure
|
||||
$FilesRemovedFailure += [PSCustomObject]@{
|
||||
ComputerName = $File.PSComputerName;
|
||||
Name = $File.Fullname;
|
||||
Timestamp = (Get-Date).tostring("yyyy/MM/dd HH:mm:ss")
|
||||
}
|
||||
}
|
||||
}
|
||||
Default {
|
||||
try {
|
||||
Invoke-Command @InvokeCommandParams
|
||||
Write-Verbose -Message ("Process~" + $File.PSComputerName + "~" + $File.FullName + " removed")
|
||||
# log success
|
||||
$FilesRemovedSuccess += [PSCustomObject]@{
|
||||
ComputerName = $File.PSComputerName;
|
||||
Name = $File.Fullname;
|
||||
Timestamp = (Get-Date).tostring("yyyy/MM/dd HH:mm:ss")
|
||||
}
|
||||
Write-Output $File
|
||||
}
|
||||
catch {
|
||||
Write-Verbose -Message ("Start~" + $File.PSComputerName + "~" + $File.FullName + " failure")
|
||||
# log failure
|
||||
$FilesRemovedFailure += [PSCustomObject]@{
|
||||
ComputerName = $File.PSComputerName;
|
||||
Name = $File.Fullname;
|
||||
Timestamp = (Get-Date).tostring("yyyy/MM/dd HH:mm:ss")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Write-Verbose -Message ("End~" + $File.PSComputerName + "~" + $File.FullName )
|
||||
}
|
||||
}
|
||||
End {
|
||||
Write-Verbose -Message "End Remove-ITDExpiredFilesAuto"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Introduction
|
||||
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
|
||||
|
||||
# Getting Started
|
||||
TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
|
||||
1. Installation process
|
||||
2. Software dependencies
|
||||
3. Latest releases
|
||||
4. API references
|
||||
|
||||
# Build and Test
|
||||
TODO: Describe and show how to build your code and run the tests.
|
||||
|
||||
# Contribute
|
||||
TODO: Explain how other users and developers can contribute to make your code better.
|
||||
|
||||
If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
|
||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
||||
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.ITD-WindowsServer.General'
|
||||
|
||||
variables:
|
||||
major: 0
|
||||
minor: 7
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.ITD-WindowsServer.General.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.ITD-WindowsServer.General.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.ITD-WindowsServer.General'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.ITD-WindowsServer.General).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
Test-MyTestFunction -Verbose
|
||||
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
|
||||
#>
|
||||
|
||||
function New-ITDWindowsVM {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('VMware', 'Azure')]
|
||||
[string]
|
||||
$Platform,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]
|
||||
$CPU,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]
|
||||
$MemoryGB,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]
|
||||
$DiskOS,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]
|
||||
$DiskSwap,
|
||||
|
||||
[int]
|
||||
$DiskData = 0,
|
||||
|
||||
[Parameter(Mandatory = $true)] # this will decide Azure subscription
|
||||
[string]
|
||||
$Subnet,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$OS,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$Environment,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$Datacenter,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$LicensingRestrictions,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
switch ($Platform) {
|
||||
'VMware' {
|
||||
# New-ITDWindowsVmVmware
|
||||
}
|
||||
'Azure' {
|
||||
# New-ITDWindowsVmAzure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+694
@@ -0,0 +1,694 @@
|
||||
<# ################
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
$NewVMWindowsAzureParams = @{
|
||||
ComputerName = 'itdzmtest701.nd.gov';
|
||||
ResourceGroupNameOverride = 'rg-shared-iis-tst';
|
||||
AvailabilityZone = 2;
|
||||
CPU = 2;
|
||||
MemoryGB = 16;
|
||||
DiskOsGB = 128;
|
||||
DiskDataGB = 0;
|
||||
Subnet = '10.21.8.0/22';
|
||||
OS = 'Windows Server 2022 Datacenter';
|
||||
Environment = 'Test';
|
||||
AppName = 'ITD-POC-zmeier';
|
||||
LicensingRestrictions = "No Licensing Restrictions";
|
||||
}
|
||||
|
||||
New-ITDWindowsVmAzure @NewVMWindowsAzureParams -Credential $PrvCred -Verbose
|
||||
#>
|
||||
|
||||
function New-ITDWindowsVmAzure {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[string]
|
||||
$ResourceGroupNameOverride,
|
||||
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[ValidateSet(1, 2, 3)]
|
||||
[int]
|
||||
$AvailabilityZone,
|
||||
|
||||
[int]
|
||||
$CPU,
|
||||
|
||||
[int]
|
||||
$MemoryGB,
|
||||
|
||||
[int]
|
||||
$DiskOsGB,
|
||||
|
||||
[int]
|
||||
$DiskDataGB,
|
||||
|
||||
[string]
|
||||
$Subnet,
|
||||
|
||||
[string]
|
||||
$OS,
|
||||
|
||||
[string]
|
||||
$Environment,
|
||||
|
||||
#[string]
|
||||
#$Subscription,
|
||||
|
||||
[string]
|
||||
$LicensingRestrictions,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
$ComputerName = $ComputerName.ToLower()
|
||||
$FQDN = $ComputerName
|
||||
$Hostname = $FQDN.split('.')[0]
|
||||
|
||||
Write-Verbose -Message "Prepare Connections"
|
||||
#$tenantId = '2dea0464-da51-4a88-bae2-b3db94bc0c54'
|
||||
#$AppId = '60244573-7130-4026-9c6d-47de73f8ca29'
|
||||
#$SecureStringPwd = '' #$Secret:AzureVMServicePrincipal #Pqt8Q~E-dDmQugcPPWdaK2t_4retS41VVVVOZbOx
|
||||
#$SecureStringPwd = 'Pqt8Q~E-dDmQugcPPWdaK2t_4retS41VVVVOZbOx'
|
||||
#$PSCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AppId, ($SecureStringPwd | ConvertTo-SecureString -AsPlainText -Force)
|
||||
#Connect-AzAccount -ServicePrincipal -Credential $PSCredential -Tenant $tenantId
|
||||
|
||||
Write-Verbose -Message "Prepare Credentials"
|
||||
$RadiusCred = New-Object System.Management.Automation.PSCredential($Credential.username.split('\')[1], ($Credential.Password))
|
||||
|
||||
Write-Verbose -Message "Infoblox: Find DNS pre-existing record, or create one"
|
||||
Clear-DnsClientCache
|
||||
$Cidr = $Subnet
|
||||
[Net.IpAddress]$NetworkId = $Cidr.split('/')[0]
|
||||
#######
|
||||
####### Remove 10.10.10.10 references when DNS sync is fixed
|
||||
#######
|
||||
[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -Server 10.10.10.10 -ErrorAction SilentlyContinue).IPAddress
|
||||
$SubnetMaskInt = $CIDR.split('/')[1]
|
||||
$Int64 = ([convert]::ToInt64(('1' * $SubnetMaskInt + '0' * (32 - $SubnetMaskInt)), 2))
|
||||
[Net.IPAddress]$SubnetMask = '{0}.{1}.{2}.{3}' -f ([math]::Truncate($Int64 / 16777216)).ToString(),
|
||||
([math]::Truncate(($Int64 % 16777216) / 65536)).ToString(),
|
||||
([math]::Truncate(($Int64 % 65536) / 256)).ToString(),
|
||||
([math]::Truncate($Int64 % 256)).ToString()
|
||||
$IPSplit = $Subnet.Split('.')
|
||||
[Net.IPAddress]$DefaultGateway = ($IPSplit[0] + '.' + $IPSplit[1] + '.' + $IPSplit[2] + '.' + (($CIDR.split('/')[0].split('.')[-1] -as [int]) + 1) )
|
||||
|
||||
If ($null -ne $IpAddress) {
|
||||
If (($IpAddress.Address -band $SubnetMask.Address) -eq ($NetworkId.Address -band $SubnetMask.Address)) {
|
||||
Write-Warning "DNS record already exists, CIDR Block match"
|
||||
}
|
||||
Else {
|
||||
Write-Error "DNS record already exists, and does not match CIDR Block"
|
||||
Break
|
||||
}
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Pre-existing IP address not found, creating new DNS record."
|
||||
New-ITDIbDNSRecordNextAvailableIP -Hostname $FQDN -CIDR $CIDR -Credential $RadiusCred
|
||||
Start-Sleep -Seconds 5
|
||||
Write-Verbose -Message ("FQDN is " + $FQDN)
|
||||
#######
|
||||
####### Remove 10.10.10.10 references when DNS sync is fixed
|
||||
#######
|
||||
#[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction Stop -Server 10.10.10.10).IPAddress
|
||||
|
||||
[Net.IPAddress]$IpAddress = (Get-ITDIbDNSRecord -Hostname $FQDN -Credential $RadiusCred).IPv4Address
|
||||
|
||||
If ((Test-NetConnection -ComputerName $IpAddress.IPAddressToString).PingSucceeded) {
|
||||
Write-Error "IP Address already in use." -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Passwordstate: If local administrator password does not exist in vault, create it."
|
||||
If ($FQDN -like "itdcnd*") {
|
||||
$PasswordStateList = "Peoplesoft Share PW"
|
||||
}
|
||||
Else {
|
||||
$PasswordStateList = "CSRC"
|
||||
}
|
||||
|
||||
$GuestVMLocalCredential = Get-ITDPassword -Title $FQDN -UserName itdadmin -Credential $Credential -ErrorAction SilentlyContinue
|
||||
If ($GuestVMLocalCredential) {
|
||||
Write-Verbose -Message "Passwordstate: Local admin password record already exists, use those credentials"
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Passwordstate: Local admin password record does not exist, creating new credentials"
|
||||
$GuestVMLocalCredential = New-ITDPassword -Title $FQDN -UserName itdadmin -Description 'Local Administrator' -PasswordList $PasswordStateList -Credential $Credential
|
||||
}
|
||||
$GuestCredentialAB = New-Object System.Management.Automation.PSCredential ('itdadmin', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
$GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
|
||||
Write-Verbose -Message "Determine environment"
|
||||
switch ($Environment) {
|
||||
{ $_ -eq 'Test' -or $_ -eq 'Development' } {
|
||||
Write-Verbose -Message "Environment is Test or Development"
|
||||
$EnvShortString = 'tst'
|
||||
}
|
||||
'Production' {
|
||||
Write-Verbose -Message "Environment is Production"
|
||||
$EnvShortString = 'prd'
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Determine subscription, via Subnet"
|
||||
$AllSubscriptions = Get-AzSubscription
|
||||
$AllAzVirtualNetworks = ForEach ($Subscription in $AllSubscriptions) {
|
||||
Set-AzContext -SubscriptionObject $Subscription | Out-Null
|
||||
Get-AzVirtualNetwork | ForEach-Object {
|
||||
[PSCustomObject]@{
|
||||
Subscription = $Subscription.Name;
|
||||
VirtualNetwork = $_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Subscription = ($AllAzVirtualNetworks | Where-Object { $_.VirtualNetwork.Subnets.AddressPrefix -match $Subnet }).Subscription
|
||||
$VirtualNetwork = ($AllAzVirtualNetworks | Where-Object { $_.VirtualNetwork.Subnets.AddressPrefix -match $Subnet }).VirtualNetwork
|
||||
$VirtualNetworkSubnet = $VirtualNetwork.Subnets | Where-Object { $_.AddressPrefix -match $Subnet }
|
||||
|
||||
$VNetName = $VirtualNetwork.Name
|
||||
$VNetSubnet = $VirtualNetworkSubnet.Name
|
||||
|
||||
switch ($OS) {
|
||||
"Windows Server 2019 Datacenter" {
|
||||
$VMOS = "Windows"
|
||||
$Publisher = "MicrosoftWindowsServer"
|
||||
$Offer = "WindowsServer"
|
||||
$sku = "2019-Datacenter"
|
||||
}
|
||||
"Windows Server 2022 Datacenter" {
|
||||
$VMOS = "Windows"
|
||||
$Publisher = "MicrosoftWindowsServer"
|
||||
$Offer = "WindowsServer"
|
||||
$sku = "2022-datacenter"
|
||||
}
|
||||
Default { Write-Error "Invalid operating system" -ErrorAction Stop }
|
||||
}
|
||||
|
||||
# finalize VM location and size
|
||||
$location = "centralus"
|
||||
$VMSizeFilter1 = @(
|
||||
"Standard_D2ds_v5",
|
||||
"Standard_D4ds_v5",
|
||||
"Standard_D8ds_v5",
|
||||
"Standard_D16ds_v5",
|
||||
"Standard_D32ds_v5",
|
||||
"Standard_E2ds_v5",
|
||||
"Standard_E4ds_v5",
|
||||
"Standard_E8ds_v5",
|
||||
"Standard_E16ds_v5",
|
||||
"Standard_E20ds_v5",
|
||||
"Standard_E32ds_v5",
|
||||
"Standard_F2s_v2",
|
||||
"Standard_F4s_v2",
|
||||
"Standard_F8s_v2",
|
||||
"Standard_F16s_v2",
|
||||
"Standard_F32s_v2"
|
||||
)
|
||||
|
||||
$VMSize = Get-AzVMSize -Location centralus | `
|
||||
Where-Object { $VMSizeFilter1 -contains $_.Name } | `
|
||||
Where-Object { $_.NumberOfCores -ge $CPU -and $_.MemoryInMB -ge ($Memory * 1024) } | `
|
||||
Where-Object Name -NotMatch "_Promo" | `
|
||||
Sort-Object NumberOfCores, MemoryInMB | `
|
||||
Select-Object -First 1
|
||||
|
||||
Write-Verbose -Message "Determine ResourceGroupName, and create Resource Group if needed"
|
||||
If ($PSBoundParameters.ContainsKey('ResourceGroupNameOverride')) {
|
||||
# use the name in the variable
|
||||
Write-Verbose -Message "ResourceGroupName parameter found, is $ResourceGroupNameOverride"
|
||||
$ResourceGroupName = $ResourceGroupNameOverride
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "ResourceGroupName parameter not found, determine now"
|
||||
# if name is not given, determine the name
|
||||
$VMOwner = $AppName.split('-')[0].ToLower()
|
||||
$VMFunction = (($AppName -replace "$VMOwner-") -replace "-").ToLower() -replace " "
|
||||
$ResourceGroupName = "rg-$VMOwner-$VMFunction-$EnvShortString"
|
||||
}
|
||||
Write-Verbose -Message "ResourceGroupName is $ResourceGroupName"
|
||||
|
||||
Write-Verbose -Message "Change to selected subscription and validate resource group exists"
|
||||
Set-AzContext $Subscription | Out-Null
|
||||
$ResGroupExist = $null
|
||||
$ResGroupExist = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
|
||||
|
||||
If (@($ResGroupExist).count -gt 1) {
|
||||
Write-Error "Multiple Resource Groups matched" -ErrorAction Stop
|
||||
}
|
||||
If (@($ResGroupExist).count -lt 1) {
|
||||
Write-Warning "No matching resource group found, creating it now"
|
||||
New-AzResourceGroup -Name $ResourceGroupName -Location $Location -Tag @{ApplicationName = $AppName }
|
||||
}
|
||||
If (@($ResGroupExist).count -eq 1) {
|
||||
Write-Verbose -Message "Exactly one matching resource group found"
|
||||
}
|
||||
|
||||
$VMName = "vm-$HostName-$EnvShortString"
|
||||
$VMIPConfigName = "ipconfig-$HostName-$EnvShortString"
|
||||
$VMNicName = "nic-$HostName-$EnvShortString"
|
||||
$VMObjectName = "vm-$HostName-$EnvShortString"
|
||||
$VMOsDiskName = "vm-$HostName-os-$EnvShortString"
|
||||
|
||||
Write-Verbose -Message "Verifying Applicable Inputs are Lowercase"
|
||||
$VMIPConfigName = $VMIPConfigName.ToLower()
|
||||
$VMNicName = $VMNicName.ToLower()
|
||||
$VMObjectName = $VMObjectName.ToLower()
|
||||
$VMOsDiskName = $VMOSDiskName.ToLower()
|
||||
|
||||
Write-Verbose -Message "Creating IPConfig and NIC"
|
||||
$IPConfig = New-AzNetworkInterfaceIpConfig -Name $VMIPConfigName -PrivateIpAddress $IPAddress.IPAddressToString -PrivateIpAddressVersion IPv4 -Subnet $VirtualNetworkSubnet
|
||||
$VMNIC = New-AzNetworkInterface -IpConfigurationName $IPConfig -Location $Location -Name $VMNICName -ResourceGroupName $ResourceGroupName -Subnet $VirtualNetworkSubnet -Force
|
||||
$VMNIC.IpConfigurations[0].PrivateIpAllocationMethod = "Static"
|
||||
$VMNIC.IpConfigurations[0].PrivateIpAddress = $IPAddress.IPAddressToString
|
||||
Set-AzNetworkInterface -NetworkInterface $VMNIC
|
||||
|
||||
Write-Verbose -Message "Build VM Config"
|
||||
$vmConfigParams = @{
|
||||
VMName = $VMObjectName
|
||||
VMSize = $VMSize.Name
|
||||
}
|
||||
|
||||
If ($PSBoundParameters.ContainsKey('AvailabilityZone')) {
|
||||
Write-Verbose -Message "AvailabilityZone parameter found, adding to vmConfigParams"
|
||||
$vmConfigParams += @{
|
||||
Zone = $AvailabilityZone
|
||||
}
|
||||
}
|
||||
|
||||
$vmConfig = New-AzVMConfig @vmConfigParams
|
||||
$vmConfig | Set-AzVMOSDisk -Name $VMOSDiskName -CreateOption FromImage
|
||||
If ($VMOS -eq "Windows") {
|
||||
$vmConfig | Set-AzVMOperatingSystem -Windows -ComputerName $VMName -Credential $GuestVMLocalCredential
|
||||
$vmConfig | Set-AzVMSourceImage -PublisherName $Publisher -Offer $Offer -Skus $Sku -Version latest
|
||||
$vmConfig.OSProfile.ComputerName = $HostName
|
||||
}
|
||||
If ($VMOS -eq "Linux") {
|
||||
$vmConfig | Set-AzVMOperatingSystem -Linux -ComputerName $VMFQDN -Credential $GuestVMLocalCredential
|
||||
Switch ($VMSubscription) {
|
||||
"npd01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/76297098-764c-43de-8525-c9fda1b237be/resourceGroups/rg-infra-templates-tst-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-103" }
|
||||
"infra01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/e53aa0c7-824d-40a2-b420-4ab77b1051d2/resourceGroups/rg-infra-templates-prd-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-403" }
|
||||
"prd01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/437b2bfa-850e-4464-b6c2-38a68cda7c69/resourceGroups/rg-infra-templates-prd-002/providers/Microsoft.Compute/images/vm-rhel74template-prd-003" }
|
||||
}
|
||||
}
|
||||
|
||||
$vmConfig | Add-AzVMNetworkInterface -Id $VMNIC.ID
|
||||
Set-AzVMBootDiagnostic -VM $vmConfig -Enable -ResourceGroupName $ResourceGroupName
|
||||
|
||||
Write-Verbose "Creating VM"
|
||||
New-AzVM -VM $vmConfig -ResourceGroupName $ResourceGroupName -Location $Location -DisableBginfoExtension -LicenseType "Windows_Server" #-AsJob
|
||||
|
||||
Start-Sleep -Seconds 60
|
||||
|
||||
$VM = Get-AzVM -Name $VMObjectName -ResourceGroupName $ResourceGroupName
|
||||
|
||||
If ($PSBoundParameters.ContainsKey("DiskDataGB")) {
|
||||
$ExistingDisks = @($VM.StorageProfile.DataDisks | Select-Object *, @{n = 'ItdId'; e = { [int]($_.Name -replace "vm-$hostname-app-$environment-") } })
|
||||
|
||||
$NewDiskItdIdInt = ($ExistingDisks | Sort-Object ItdId -Descending | Select-Object -First 1).ItdId + 1
|
||||
$NewDiskItdIdStr = $NewDiskItdIdInt.ToString("000")
|
||||
$NewDiskName = "vm-$Hostname-app-$EnvShortString-$NewDiskItdIdStr" #vm-itduc4p1-app-tst-001
|
||||
|
||||
$LunID = ($ExistingDisks | Sort-Object Lun -Descending | Select-Object -First 1).Lun + 1
|
||||
|
||||
$count = 0
|
||||
|
||||
If ($ExistingDisks) {
|
||||
while ($Size -match $ExistingDisks.DiskSizeGB) {
|
||||
$count++
|
||||
Write-Verbose -Message "SizeGB: $Size, Count: $count"
|
||||
If ($count -ge 11) {
|
||||
Write-Error "Disk size not available" -ErrorAction Stop
|
||||
}
|
||||
Else {
|
||||
$Size = $Size - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "SizeGB: $Size, Count: $count"
|
||||
|
||||
$AzureRmDiskConfigParams = @{
|
||||
DiskSizeGB = $DiskDataGB
|
||||
Location = $Location
|
||||
CreateOption = "Empty"
|
||||
SkuName = "Premium_LRS"
|
||||
}
|
||||
|
||||
If ($Zone) {
|
||||
Write-Verbose "VM is located in Zone $Zone"
|
||||
$AzureRmDiskConfigParams += @{Zone = $Zone }
|
||||
}
|
||||
#$DiskConfig = New-AzureRmDiskConfig -DiskSizeGB $Size -Location $Location -CreateOption Empty -SkuName Premium_LRS
|
||||
$DiskConfig = New-AzDiskConfig @AzureRmDiskConfigParams
|
||||
|
||||
If (!(Get-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $NewDiskName -ErrorAction SilentlyContinue)) {
|
||||
$NewDisk = New-AzDisk -DiskName $NewDiskName -Disk $DiskConfig -ResourceGroupName $ResourceGroupName
|
||||
|
||||
$VM = Add-AzVMDataDisk -Name $NewDiskName -CreateOption Attach -ManagedDiskId $NewDisk.Id -VM $VM -Lun $LunID -Caching ReadOnly
|
||||
Update-AzVM -VM $VM -ResourceGroupName $ResourceGroupName -AsJob
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Wait two minutes before Pre-Firewall Guest OS customization"
|
||||
Start-Sleep -Seconds 120
|
||||
|
||||
Write-Verbose -Message "Begin Pre-Firewall Guest OS customization"
|
||||
$InvokeAzVMRunCommandParams = @{
|
||||
ResourceGroupName = $ResourceGroupName;
|
||||
Name = $VMName;
|
||||
CommandId = 'RunPowerShellScript'
|
||||
}
|
||||
|
||||
Write-Verbose -Message "1-Set WMI Tags"
|
||||
$ScriptBlock = {
|
||||
try {
|
||||
Write-Verbose "Create new Class"
|
||||
$Class = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
|
||||
|
||||
$Class["__CLASS"] = "ITD";
|
||||
$Class.Qualifiers.Add("Static", $true)
|
||||
$Class.Properties.Add("MyKey", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties["MyKey"].Qualifiers.Add("Key", $true)
|
||||
|
||||
$Class.Properties.Add("LastModified", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties.Add("DTAP", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties.Add("Baseline", [System.Management.CimType]::String, $false)
|
||||
|
||||
$Class.Put()
|
||||
|
||||
Write-Verbose "Create single ITD Object"
|
||||
Set-WmiInstance -Class ITD -Arguments @{LastModified = (Get-Date); DTAP = "Prod"; Baseline = "000" }
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "3-Disk Configuration"
|
||||
$ScriptBlock = {
|
||||
try {
|
||||
# Non-initialized and MBR-initialized disks will have 0 partitions by default, but GPT-initialized disks will have 1 system reserved partition by default
|
||||
$disks = Get-Disk | Where-Object { $_.NumberOfPartitions -eq 0 -or ( $_.PartitionStyle -eq 'GPT' -and $_.NumberOfPartitions -eq 1 ) } | Sort-Object -Property Number
|
||||
|
||||
if ($disks) {
|
||||
Write-Verbose -Message "Found $(@($disks).Count) unpartitioned disks."
|
||||
|
||||
# Prevent the "You must format this partition before using it." popup
|
||||
if (Get-Service ShellHWDetection -ErrorAction SilentlyContinue) { Stop-Service ShellHWDetection -ErrorAction SilentlyContinue }
|
||||
|
||||
foreach ($disk in $disks) {
|
||||
if ($disk.IsOffline) {
|
||||
Set-Disk $disk.Number -IsOffline $false
|
||||
Write-Verbose -Message "Brought disk $($disk.Number)($("{0:n0}GB" -f ($disk.Size / 1GB))) online..."
|
||||
}
|
||||
|
||||
if ($disk.IsReadOnly) { Set-Disk $disk.Number -IsReadOnly $false }
|
||||
if ($disk.PartitionStyle -eq 'RAW') { Initialize-Disk $disk.Number -PartitionStyle GPT -ErrorAction SilentlyContinue }
|
||||
|
||||
$diskParam = @{
|
||||
FileSystem = 'NTFS'
|
||||
Confirm = $false
|
||||
}
|
||||
|
||||
$driveLetter = [Int][Char]'D'
|
||||
while (Get-Volume -DriveLetter $([Char]$driveLetter) -ErrorAction SilentlyContinue) {
|
||||
$driveLetter++
|
||||
}
|
||||
|
||||
$diskParam.DriveLetter = [Char]$driveLetter
|
||||
|
||||
if (@($disks).IndexOf($disk) -eq 0 -and (-not (Get-Volume -DriveLetter D -ErrorAction SilentlyContinue))) {
|
||||
$diskParam.NewFileSystemLabel = 'Temporary Storage'
|
||||
}
|
||||
elseif (@($disks).IndexOf($disk) -eq 1 -and (-not (Get-Volume -DriveLetter E -ErrorAction SilentlyContinue))) {
|
||||
$diskParam.NewFileSystemLabel = 'Data'
|
||||
}
|
||||
|
||||
[void](New-Partition -DiskNumber $disk.Number -DriveLetter $diskParam.DriveLetter -UseMaximumSize)
|
||||
[void](Format-Volume @diskParam)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "No unpartitioned disks found, continuing..."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
finally {
|
||||
if (Get-Service ShellHWDetection -ErrorAction SilentlyContinue) { Start-Service ShellHWDetection -ErrorAction SilentlyContinue }
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "5-Time Zone"
|
||||
$ScriptBlock = {
|
||||
if ((Get-TimeZone).Id -ne 'Central Standard Time') {
|
||||
Write-Verbose -Message "Current time zone set to $((Get-TimeZone).Id), setting to Central Standard Time."
|
||||
|
||||
Set-TimeZone -Id 'Central Standard Time'
|
||||
|
||||
Write-Verbose -Message "Time zone set to Central Standard Time."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Time zone is already set to Central Standard Time."
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "6-Enable Performance Counters"
|
||||
$ScriptBlock = {
|
||||
# Enable Performance Counters
|
||||
try {
|
||||
if (Get-ScheduledTask -TaskName "Server Manager Performance Monitor" | Where-Object State -NE "Running" -ErrorAction SilentlyContinue) {
|
||||
Enable-ScheduledTask -TaskPath "\Microsoft\Windows\PLA\" -TaskName "Server Manager Performance Monitor" | Start-ScheduledTask
|
||||
|
||||
Write-Verbose -Message "Performance monitors enabled."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Performance monitors already enabled, continuing..."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "7-Disable Windows Firewall"
|
||||
$ScriptBlock = {
|
||||
# Disable Windows Firewall
|
||||
Write-Verbose -Message "Checking for active Windows Firewall..."
|
||||
|
||||
if ((Get-NetFirewallProfile).Enabled -contains 'True') {
|
||||
Write-Verbose -Message "Windows Firewall is still enabled, disabling it..."
|
||||
|
||||
Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
|
||||
|
||||
Write-Verbose -Message "Windows Firewall disabled."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Windows Firewall already disabled, continuing..."
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
# determine Active Directory Forest and OU
|
||||
Write-Verbose -Message "8a-Determine domain join"
|
||||
$DomainName = $FQDN.Substring($FQDN.IndexOf(".") + 1)
|
||||
switch ($DomainName) {
|
||||
'nd.gov' {
|
||||
$SearchBaseDomain = "dc=nd,dc=gov"
|
||||
}
|
||||
'ndcloud.gov' {
|
||||
$SearchBaseDomain = "dc=ndcloud,dc=gov"
|
||||
}
|
||||
}
|
||||
|
||||
If ($DomainName -eq "nd.gov") {
|
||||
$OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq $AppName }
|
||||
If (!($OUAppName)) {
|
||||
$OUAppName = Get-ADOrganizationalUnit -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq 'All-General' }
|
||||
}
|
||||
$ExistingADComputer = Get-ADComputer -Filter { Name -eq $Hostname }
|
||||
If ($ExistingADComputer) {
|
||||
If ($ExistingADComputer.DistinguishedName -like ("*" + $AppName + "*") -or $ExistingADComputer.DistinguishedName -like "*All-General*") {
|
||||
Write-Warning "AD object already exists, OU path does match"
|
||||
$OuFinal = $ExistingADComputer.DistinguishedName -replace '^.+?(?<!\\),', ''
|
||||
}
|
||||
Else {
|
||||
Write-Error "AD object already exists, OU path mismatch"
|
||||
Exit
|
||||
Exit
|
||||
}
|
||||
}
|
||||
Else {
|
||||
switch ($Environment) {
|
||||
'Test' {
|
||||
If ($AppName -like "Shared-Peoplesoft*") { $EnvString = "Non-Prod" }
|
||||
Else { $EnvString = "Test" }
|
||||
$OuAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -EQ "$EnvString"
|
||||
}
|
||||
'Production' {
|
||||
If ($AppName -like "Shared-Peoplesoft*") { $EnvString = "Prod" }
|
||||
Else { $EnvString = "Prod" }
|
||||
|
||||
$OuAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -EQ "$EnvString"
|
||||
}
|
||||
}
|
||||
If ($OuAppNameEnv) { $OUFinal = $OUAppNameEnv.DistinguishedName }
|
||||
Else { $OuFinal = $OUAppName.DistinguishedName }
|
||||
}
|
||||
}
|
||||
|
||||
# wait for firewall and reconnect
|
||||
Write-Verbose -Message "8b-Verify if firewall is open before domain join attempt"
|
||||
$connectivity = $false
|
||||
while ($connectivity -eq $false) {
|
||||
If (Test-NetConnection -ComputerName $IpAddress) {
|
||||
Write-Verbose -Message "Ping successful" -Verbose
|
||||
$connectivity = $true
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Ping failed, may need to wait for PA IP sync" -Verbose
|
||||
Start-Sleep -Seconds 60
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "8c-Attempt domain join"
|
||||
switch ($DomainName) {
|
||||
'nd.gov' {
|
||||
Write-Verbose -Message "Attempting domain join nd.gov"
|
||||
Write-Verbose -Message "Domain: $DomainName"
|
||||
Write-Verbose -Message "OuPath: $OuFinal"
|
||||
<#Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptPath 'C:\AzWinBuild\8-Domain-ndgov.ps1' -Parameter @{
|
||||
"DomainName" = 'nd.gov';
|
||||
"OuPath" = "$OuFinal"
|
||||
}#>
|
||||
$ScriptBlock = {
|
||||
Param(
|
||||
[string]
|
||||
$DomainName,
|
||||
|
||||
[string]
|
||||
$OuPath
|
||||
)
|
||||
|
||||
Write-Host $OuPath
|
||||
Test-NetConnection -ComputerName nd.gov
|
||||
|
||||
$DomainJoinCred = New-Object System.Management.Automation.PSCredential('ndgov\svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force))
|
||||
Add-Computer -DomainName $DomainName -OUPath $OuPath -Credential $DomainJoinCred
|
||||
|
||||
Restart-Computer -Force
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock -Parameter @{
|
||||
"DomainName" = 'nd.gov';
|
||||
"OuPath" = "$OuFinal"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 120
|
||||
|
||||
Write-Verbose -Message "10a-Copy SCCM client from itdsccmp2.nd.gov" -Verbose
|
||||
$ScriptBlock = { #### NEEDS WORK
|
||||
Param(
|
||||
|
||||
)
|
||||
|
||||
Copy-Item -path "\\itdsccmp2.nd.gov\SCCM_Client\Client\" -Destination C:\temp\SCCM_Client -Recurse
|
||||
}
|
||||
|
||||
$CopySuccess = $false
|
||||
$CopyAttempts = 0
|
||||
While ($CopySuccess -eq $false -and $CopyAttempts -lt 100) {
|
||||
$Attempts++
|
||||
$InvokeResult = Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
If ($InvokeResult.value.message -like "Copy-Item : Cannot find path*") {
|
||||
Write-Verbose -Message "SCCM Client file copy failed. Looping until it works." -Verbose
|
||||
$CopySuccess = $false
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "SCCM Client file copy success." -Verbose
|
||||
$CopySuccess = $true
|
||||
}
|
||||
}
|
||||
|
||||
# Check if SCCM automatically installed the SCCM client and registered it
|
||||
$CcmRegistered = $false
|
||||
$CcmRegistration = Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString {
|
||||
Get-Content C:\Windows\CCM\Logs\ClientIDManagerStartup.log | Select-String RegTask
|
||||
}
|
||||
If ($CcmRegistration.value.message -match "Client is registered") {
|
||||
Write-Verbose "Client is registered."
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
ElseIf ($CcmRegistration.value.message -match "Client is already registered") {
|
||||
Write-Verbose "Client is already registered."
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
If ($CcmRegistered -eq $false) {
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString {
|
||||
|
||||
If (Get-Process -Name ccmsetup -ErrorAction SilentlyContinue) {
|
||||
Write-Warning "CCM client is already installing"
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
ElseIf (Get-Process -Name ccmexec -ErrorAction SilentlyContinue) {
|
||||
Write-Warning "CCM client is already installed"
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
Else {
|
||||
Write-Warning -Message "Installing SCCM Client..."
|
||||
Invoke-Expression -Command "C:\temp\SCCM_Client\ccmsetup.exe SMSSITECODE=ITD SMSMP=itdsccmp2.nd.gov DNSSUFFIX=nd.gov"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "[$FQDN]:Approve SCCM Client"
|
||||
#Start-Sleep -Seconds 30 # ADD LOOP/SMARTS TO WAIT FOR DISCOVERY AND ANOTHER FOR APPROVAL
|
||||
Invoke-Command -ComputerName itdsccmp2.nd.gov -Credential $Credential -ArgumentList $Hostname -ScriptBlock {
|
||||
Import-Module 'D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
|
||||
$PSDrives = Get-PSDrive
|
||||
If ($PSDrives | Where-Object Name -EQ "ITD") {
|
||||
# ITD Drive exists, do nothing
|
||||
}
|
||||
else {
|
||||
New-PSDrive -Name "ITD" -PSProvider AdminUI.PS.Provider\CMSite -Root itdsccmp2.nd.gov
|
||||
}
|
||||
|
||||
Set-Location ITD:\
|
||||
$Device = Get-CMDevice -Name $args[0]
|
||||
If ($Device.IsApproved -eq 0) {
|
||||
Approve-CMDevice -DeviceName $args[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+1037
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.ITD-WindowsServer.General</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>Functions for Windows Server General administration</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,132 @@
|
||||
#
|
||||
# Module manifest for module 'ITD.ITD-WindowsServer.General'
|
||||
#
|
||||
# Generated by: zmeier
|
||||
#
|
||||
# Generated on: 6/14/2022
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'ITD.ITD-WindowsServer.General.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
|
||||
# Supported PSEditions
|
||||
CompatiblePSEditions = 'Desktop', 'Core'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '3bb063c4-d6c3-4775-8f25-4bdf31fe4b05'
|
||||
|
||||
# Author of this module
|
||||
Author = 'zmeier'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'State of North Dakota'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) zmeier. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'Functions for Windows Server general administration'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#Get public and private function definition files.
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
|
||||
|
||||
#Dot source the files
|
||||
Foreach($import in @($Public + $Private))
|
||||
{
|
||||
Try
|
||||
{
|
||||
. $import.fullname
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Here I might...
|
||||
# Read in or create an initial config file and variable
|
||||
# Export Public functions ($Public.BaseName) for WIP modules
|
||||
# Set variables visible to the module and its functions only
|
||||
|
||||
Export-ModuleMember -Function $Public.Basename
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates a Certificate Signing Request based on values inputted. Any values not inputted will result in the use of default values.
|
||||
.DESCRIPTION
|
||||
Generates a Certificate Signing Request based on values inputted. Any values not inputted will result in the use of default values. CSR will be printed to the screen, but can be saved to the clipboard, or to a file.
|
||||
Default values are:
|
||||
|
||||
.NOTES
|
||||
Run as administrator is required.
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov'
|
||||
CSR is generated using the common name shown, and default values for everything else
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov' -Organization "OrgNameHere" -OrganizationalUnit "OrgUnitHere" -Locality Mandan -State ND -Country US -KeyLength 4096
|
||||
CSR is generated using the values specified, defaults for the rest
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov' -Organization "OrgNameHere" -OrganizationalUnit "OrgUnitHere" -Locality Mandan -State ND -Country US -KeyLength 4096 -ToClipboard
|
||||
CSR is generated using the values specified, defaults for the rest, and saved into the user's clipboard
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov' -ToPath C:\temp.csr
|
||||
CSR is generated using the common name shown, and default values for everything else, and saves the CSR to a local path
|
||||
#>
|
||||
|
||||
function New-ITDSslCertificateSigningRequest {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$CommonName,
|
||||
|
||||
[string]
|
||||
$Organization = "State of North Dakota",
|
||||
|
||||
[string]
|
||||
$OrganizationalUnit = "NDIT",
|
||||
|
||||
[string]
|
||||
$Locality = "Bismarck",
|
||||
|
||||
[string]
|
||||
$State = "ND",
|
||||
|
||||
[string]
|
||||
$Country = "US",
|
||||
|
||||
[ValidateSet(2048, 4096)]
|
||||
[int]
|
||||
$KeyLength = 4096,
|
||||
|
||||
[switch]
|
||||
$Exportable = $true,
|
||||
|
||||
[ValidateSet('sha256','sha384','sha512','md5')]
|
||||
[string]
|
||||
$HashAlgorithm = "sha256",
|
||||
|
||||
[switch]
|
||||
$ToClipboard,
|
||||
|
||||
[string]
|
||||
$ToPath
|
||||
)
|
||||
|
||||
begin {
|
||||
if (-NOT([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Write-Host "Administrator priviliges are required. Please restart this script with elevated rights." -ForegroundColor Red
|
||||
Pause
|
||||
Throw "Administrator priviliges are required. Please restart this script with elevated rights."
|
||||
}
|
||||
}
|
||||
|
||||
process {
|
||||
$UID = [guid]::NewGuid()
|
||||
$files = @{}
|
||||
$files['settings'] = "$($env:TEMP)\$($UID)-settings.inf";
|
||||
$files['csr'] = "$($env:TEMP)\$($UID)-csr.req"
|
||||
|
||||
$request = @{}
|
||||
$request['SAN'] = @{}
|
||||
|
||||
#2048, sha256
|
||||
$settingsInf = "
|
||||
[Version]
|
||||
Signature=`"`$Windows NT`$
|
||||
[NewRequest]
|
||||
KeyLength = {{KeyLength}}
|
||||
Exportable = {{Exportable}}
|
||||
MachineKeySet = TRUE
|
||||
SMIME = FALSE
|
||||
RequestType = PKCS10
|
||||
ProviderName = `"Microsoft RSA SChannel Cryptographic Provider`"
|
||||
ProviderType = 12
|
||||
HashAlgorithm = {{HashAlgorithm}}
|
||||
;Variables
|
||||
Subject = `"CN={{CN}},OU={{OU}},O={{O}},L={{L}},S={{S}},C={{C}}`"
|
||||
[Extensions]
|
||||
{{SAN}}
|
||||
;Certreq info
|
||||
;http://technet.microsoft.com/en-us/library/dn296456.aspx
|
||||
;CSR Decoder
|
||||
;https://certlogik.com/decoder/
|
||||
;https://ssltools.websecurity.symantec.com/checker/views/csrCheck.jsp
|
||||
"
|
||||
|
||||
|
||||
$request['SAN_string'] = & {
|
||||
if ($request['SAN'].Count -gt 0) {
|
||||
$san = "2.5.29.17 = `"{text}`"
|
||||
"
|
||||
Foreach ($sanItem In $request['SAN'].Values) {
|
||||
$san += "_continue_ = `"dns=" + $sanItem + "&`"
|
||||
"
|
||||
}
|
||||
return $san
|
||||
}
|
||||
}
|
||||
|
||||
$settingsInf = $settingsInf.Replace("{{CN}}", $CommonName)
|
||||
$settingsInf = $settingsInf.Replace("{{O}}", $Organization)
|
||||
$settingsInf = $settingsInf.Replace("{{OU}}", $OrganizationalUnit)
|
||||
$settingsInf = $settingsInf.Replace("{{L}}", $Locality)
|
||||
$settingsInf = $settingsInf.Replace("{{S}}", $State)
|
||||
$settingsInf = $settingsInf.Replace("{{C}}", $Country)
|
||||
$settingsInf = $settingsInf.Replace("{{SAN}}", $request['SAN_string'])
|
||||
$settingsInf = $settingsInf.Replace("{{KeyLength}}",$KeyLength)
|
||||
$settingsInf = $settingsInf.Replace("{{HashAlgorithm}}",$HashAlgorithm)
|
||||
$settingsInf = $settingsInf.Replace("{{Exportable}}",$Exportable)
|
||||
|
||||
# Save settings to file in temp
|
||||
$settingsInf > $files['settings']
|
||||
|
||||
certreq -new $files['settings'] $files['csr'] > $null
|
||||
|
||||
$CSR = Get-Content $files['csr']
|
||||
|
||||
Write-Output $CSR
|
||||
If ($ToClipboard) {
|
||||
$CSR | Set-Clipboard
|
||||
}
|
||||
If ($ToPath) {
|
||||
$CSR | Out-File -FilePath $ToPath
|
||||
}
|
||||
|
||||
$files.Values | ForEach-Object {
|
||||
Remove-Item $_ -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates a Certificate Signing Request based on values inputted. Any values not inputted will result in the use of default values.
|
||||
.DESCRIPTION
|
||||
Generates a Certificate Signing Request based on values inputted. Any values not inputted will result in the use of default values. CSR will be printed to the screen, but can be saved to the clipboard, or to a file.
|
||||
Default values are:
|
||||
|
||||
.NOTES
|
||||
Run as administrator is required.
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov'
|
||||
CSR is generated using the common name shown, and default values for everything else
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov' -Organization "OrgNameHere" -OrganizationalUnit "OrgUnitHere" -Locality Mandan -State ND -Country US -KeyLength 4096
|
||||
CSR is generated using the values specified, defaults for the rest
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov' -Organization "OrgNameHere" -OrganizationalUnit "OrgUnitHere" -Locality Mandan -State ND -Country US -KeyLength 4096 -ToClipboard
|
||||
CSR is generated using the values specified, defaults for the rest, and saved into the user's clipboard
|
||||
.EXAMPLE
|
||||
New-ITDSslCertificateSigningRequest -CommonName 'commonname.nd.gov' -ToPath C:\temp.csr
|
||||
CSR is generated using the common name shown, and default values for everything else, and saves the CSR to a local path
|
||||
#>
|
||||
|
||||
function New-ITDSslCertificateSigningRequest {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$CommonName,
|
||||
|
||||
[string]
|
||||
$Organization = "State of North Dakota",
|
||||
|
||||
[string]
|
||||
$OrganizationalUnit = "NDIT",
|
||||
|
||||
[string]
|
||||
$Locality = "Bismarck",
|
||||
|
||||
[string]
|
||||
$State = "ND",
|
||||
|
||||
[string]
|
||||
$Country = "US",
|
||||
|
||||
[ValidateSet(2048, 4096)]
|
||||
[int]
|
||||
$KeyLength = 4096,
|
||||
|
||||
[switch]
|
||||
$Exportable = $true,
|
||||
|
||||
[ValidateSet('sha256','sha384','sha512','md5')]
|
||||
[string]
|
||||
$HashAlgorithm = "sha256",
|
||||
|
||||
[switch]
|
||||
$ToClipboard,
|
||||
|
||||
[string]
|
||||
$ToPath
|
||||
)
|
||||
|
||||
begin {
|
||||
if (-NOT([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Write-Host "Administrator priviliges are required. Please restart this script with elevated rights." -ForegroundColor Red
|
||||
Pause
|
||||
Throw "Administrator priviliges are required. Please restart this script with elevated rights."
|
||||
}
|
||||
}
|
||||
|
||||
process {
|
||||
$UID = [guid]::NewGuid()
|
||||
$files = @{}
|
||||
$files['settings'] = "$($env:TEMP)\$($UID)-settings.inf";
|
||||
$files['csr'] = "$($env:TEMP)\$($UID)-csr.req"
|
||||
|
||||
$request = @{}
|
||||
$request['SAN'] = @{}
|
||||
|
||||
#2048, sha256
|
||||
$settingsInf = "
|
||||
[Version]
|
||||
Signature=`"`$Windows NT`$
|
||||
[NewRequest]
|
||||
KeyLength = {{KeyLength}}
|
||||
Exportable = {{Exportable}}
|
||||
MachineKeySet = TRUE
|
||||
SMIME = FALSE
|
||||
RequestType = PKCS10
|
||||
ProviderName = `"Microsoft RSA SChannel Cryptographic Provider`"
|
||||
ProviderType = 12
|
||||
HashAlgorithm = {{HashAlgorithm}}
|
||||
;Variables
|
||||
Subject = `"CN={{CN}},OU={{OU}},O={{O}},L={{L}},S={{S}},C={{C}}`"
|
||||
[Extensions]
|
||||
{{SAN}}
|
||||
;Certreq info
|
||||
;http://technet.microsoft.com/en-us/library/dn296456.aspx
|
||||
;CSR Decoder
|
||||
;https://certlogik.com/decoder/
|
||||
;https://ssltools.websecurity.symantec.com/checker/views/csrCheck.jsp
|
||||
"
|
||||
|
||||
|
||||
$request['SAN_string'] = & {
|
||||
if ($request['SAN'].Count -gt 0) {
|
||||
$san = "2.5.29.17 = `"{text}`"
|
||||
"
|
||||
Foreach ($sanItem In $request['SAN'].Values) {
|
||||
$san += "_continue_ = `"dns=" + $sanItem + "&`"
|
||||
"
|
||||
}
|
||||
return $san
|
||||
}
|
||||
}
|
||||
|
||||
$settingsInf = $settingsInf.Replace("{{CN}}", $CommonName)
|
||||
$settingsInf = $settingsInf.Replace("{{O}}", $Organization)
|
||||
$settingsInf = $settingsInf.Replace("{{OU}}", $OrganizationalUnit)
|
||||
$settingsInf = $settingsInf.Replace("{{L}}", $Locality)
|
||||
$settingsInf = $settingsInf.Replace("{{S}}", $State)
|
||||
$settingsInf = $settingsInf.Replace("{{C}}", $Country)
|
||||
$settingsInf = $settingsInf.Replace("{{SAN}}", $request['SAN_string'])
|
||||
$settingsInf = $settingsInf.Replace("{{KeyLength}}",$KeyLength)
|
||||
$settingsInf = $settingsInf.Replace("{{HashAlgorithm}}",$HashAlgorithm)
|
||||
$settingsInf = $settingsInf.Replace("{{Exportable}}",$Exportable)
|
||||
|
||||
# Save settings to file in temp
|
||||
$settingsInf > $files['settings']
|
||||
|
||||
certreq -new $files['settings'] $files['csr'] > $null
|
||||
|
||||
$CSR = Get-Content $files['csr']
|
||||
|
||||
Write-Output $CSR
|
||||
If ($ToClipboard) {
|
||||
$CSR | Set-Clipboard
|
||||
}
|
||||
If ($ToPath) {
|
||||
$CSR | Out-File -FilePath $ToPath
|
||||
}
|
||||
|
||||
$files.Values | ForEach-Object {
|
||||
Remove-Item $_ -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
New-ITDAutomationRecord -AppName "Windows-General" -Action "Provisioning" -Minutes 3 -Platform "PowerShell-ITD.Windows"
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
function Remove-ITDSolarwindsNode {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
}
|
||||
|
||||
process {
|
||||
|
||||
$Func = {
|
||||
Import-Module -Name ITDSolarwinds -Verbose
|
||||
$test = Get-SWNode -ComputerName $args[0]
|
||||
If ($test) {
|
||||
Write-Warning "Solarwinds node found, removing it..."
|
||||
Remove-SWNode -ComputerName $args[0]
|
||||
}
|
||||
Else {
|
||||
Write-Warning "Solarwinds node not found"
|
||||
}
|
||||
}
|
||||
|
||||
Invoke-Command -ComputerName itdslrwnds.nd.gov -ScriptBlock $Func -ArgumentList $ComputerName -Credential $Credential
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Introduction
|
||||
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
|
||||
|
||||
# Getting Started
|
||||
TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
|
||||
1. Installation process
|
||||
2. Software dependencies
|
||||
3. Latest releases
|
||||
4. API references
|
||||
|
||||
# Build and Test
|
||||
TODO: Describe and show how to build your code and run the tests.
|
||||
|
||||
# Contribute
|
||||
TODO: Explain how other users and developers can contribute to make your code better.
|
||||
|
||||
If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
|
||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
||||
@@ -0,0 +1,19 @@
|
||||
# figure out where to put it
|
||||
$x = Get-ADComputer -Filter * -Server itdk12dc7.k12.nd.us -Credential $K12Cred -Properties CanonicalName
|
||||
|
||||
|
||||
|
||||
# enter this on the machine itself
|
||||
$Credential = Get-Credential
|
||||
$AddComputerParams = @{
|
||||
DomainName = 'k12.nd.us';
|
||||
OUPath = 'OU=Prod,OU=All-General,OU=Computers,OU=ITD,DC=k12,DC=nd,DC=us' ;
|
||||
Credential = $Credential;
|
||||
}
|
||||
Add-Computer @AddComputerParams
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.ITD-WindowsServer.Lifecycle'
|
||||
|
||||
variables:
|
||||
major: 0
|
||||
minor: 1
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.ITD-WindowsServer.Lifecycle.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.ITD-WindowsServer.Lifecycle.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.ITD-WindowsServer.Lifecycle'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.ITD-WindowsServer.Lifecycle).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.ITD-WindowsServer.Lifecycle</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>Functions for Windows Server Lifecycle administration</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
#
|
||||
# Module manifest for module 'ITD.ITD-WindowsServer.Lifecycle'
|
||||
#
|
||||
# Generated by: zmeier
|
||||
#
|
||||
# Generated on: 6/14/2022
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'ITD.ITD-WindowsServer.Lifecycle.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
|
||||
# Supported PSEditions
|
||||
CompatiblePSEditions = 'Desktop', 'Core'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '42b3d283-1030-4bf9-afa7-4810061f74a8'
|
||||
|
||||
# Author of this module
|
||||
Author = 'zmeier'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'State of North Dakota'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) zmeier. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'Functions for Windows Server general administration'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#Get public and private function definition files.
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
|
||||
|
||||
#Dot source the files
|
||||
Foreach($import in @($Public + $Private))
|
||||
{
|
||||
Try
|
||||
{
|
||||
. $import.fullname
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Here I might...
|
||||
# Read in or create an initial config file and variable
|
||||
# Export Public functions ($Public.BaseName) for WIP modules
|
||||
# Set variables visible to the module and its functions only
|
||||
|
||||
Export-ModuleMember -Function $Public.Basename
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
<# ################
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
$NewVMWindowsAzureParams = @{
|
||||
ComputerName = 'itdzmtest901.nd.gov';
|
||||
CPU = 2;
|
||||
MemoryGB = 8;
|
||||
DiskOsGB = 128;
|
||||
DiskDataGB = 0;
|
||||
Subnet = '10.21.8.0/22';
|
||||
OS = "Windows Server 2025 Datacenter";
|
||||
Environment = 'Test';
|
||||
AppName = 'ITD-POC-zmeier';
|
||||
LicensingRestrictions = "No Licensing Restrictions";
|
||||
}
|
||||
|
||||
New-ITDWindowsVmAzure @NewVMWindowsAzureParams -Credential $PrvCred -Verbose
|
||||
.EXAMPLE
|
||||
$NewVMWindowsAzureParams = @{
|
||||
ComputerName = 'itdzmtest701.nd.gov';
|
||||
ResourceGroupNameOverride = 'rg-shared-iis-tst';
|
||||
AvailabilityZone = 2;
|
||||
CPU = 2;
|
||||
MemoryGB = 8;
|
||||
DiskOsGB = 128;
|
||||
DiskDataGB = 0;
|
||||
Subnet = '10.21.8.0/22';
|
||||
OS = 'Windows Server 2022 Datacenter';
|
||||
Environment = 'Test';
|
||||
AppName = 'ITD-POC-zmeier';
|
||||
LicensingRestrictions = "No Licensing Restrictions";
|
||||
}
|
||||
|
||||
New-ITDWindowsVmAzure @NewVMWindowsAzureParams -Credential $PrvCred -Verbose
|
||||
#>
|
||||
|
||||
function New-ITDWindowsVmAzureStep1 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$FQDN,
|
||||
|
||||
[string]
|
||||
$ResourceGroupNameOverride,
|
||||
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[ValidateSet(1, 2, 3)]
|
||||
[int]
|
||||
$AvailabilityZone,
|
||||
|
||||
[Parameter(ParameterSetName = 'VmSizeOverride')]
|
||||
[string]
|
||||
$VMSizeOverride,
|
||||
|
||||
[Parameter(ParameterSetName = 'VmSizeDetermine')]
|
||||
[int]
|
||||
$CPU,
|
||||
|
||||
[Parameter(ParameterSetName = 'VmSizeDetermine')]
|
||||
[int]
|
||||
$MemoryGB,
|
||||
|
||||
[int]
|
||||
$DiskOsGB,
|
||||
|
||||
[int]
|
||||
$DiskDataGB,
|
||||
|
||||
[string]
|
||||
$Subnet,
|
||||
|
||||
[string]
|
||||
$OS,
|
||||
|
||||
[string]
|
||||
$VMEnvironment,
|
||||
|
||||
#[string]
|
||||
#$Subscription,
|
||||
|
||||
[string]
|
||||
$LicensingRestrictions,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
$FQDN = $FQDN.ToLower()
|
||||
$Hostname = $FQDN.split('.')[0]
|
||||
|
||||
Write-Verbose -Message "Prepare Connections"
|
||||
#$tenantId = '2dea0464-da51-4a88-bae2-b3db94bc0c54'
|
||||
#$AppId = '60244573-7130-4026-9c6d-47de73f8ca29'
|
||||
#$SecureStringPwd = '' #$Secret:AzureVMServicePrincipal #Pqt8Q~E-dDmQugcPPWdaK2t_4retS41VVVVOZbOx
|
||||
#$SecureStringPwd = 'sQV8Q~sNLcrDl2RgB32gsSEsDVgdFhNMcBdPoaEX'
|
||||
#$PSCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AppId, ($SecureStringPwd | ConvertTo-SecureString -AsPlainText -Force)
|
||||
#Connect-AzAccount -ServicePrincipal -Credential $PSCredential -Tenant $tenantId
|
||||
|
||||
Write-Verbose -Message "Prepare Credentials"
|
||||
$RadiusCred = New-Object System.Management.Automation.PSCredential($Credential.username.split('\')[1], ($Credential.Password))
|
||||
|
||||
Write-Verbose -Message "Infoblox: Find DNS pre-existing record, or create one"
|
||||
Clear-DnsClientCache
|
||||
$Cidr = $Subnet
|
||||
[Net.IpAddress]$NetworkId = $Cidr.split('/')[0]
|
||||
#######
|
||||
####### Remove 10.10.10.10 references when DNS sync is fixed
|
||||
#######
|
||||
[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -Server 10.10.10.10 -ErrorAction SilentlyContinue).IPAddress
|
||||
$SubnetMaskInt = $CIDR.split('/')[1]
|
||||
$Int64 = ([convert]::ToInt64(('1' * $SubnetMaskInt + '0' * (32 - $SubnetMaskInt)), 2))
|
||||
[Net.IPAddress]$SubnetMask = '{0}.{1}.{2}.{3}' -f ([math]::Truncate($Int64 / 16777216)).ToString(),
|
||||
([math]::Truncate(($Int64 % 16777216) / 65536)).ToString(),
|
||||
([math]::Truncate(($Int64 % 65536) / 256)).ToString(),
|
||||
([math]::Truncate($Int64 % 256)).ToString()
|
||||
$IPSplit = $Subnet.Split('.')
|
||||
[Net.IPAddress]$DefaultGateway = ($IPSplit[0] + '.' + $IPSplit[1] + '.' + $IPSplit[2] + '.' + (($CIDR.split('/')[0].split('.')[-1] -as [int]) + 1) )
|
||||
|
||||
If ($null -ne $IpAddress) {
|
||||
If (($IpAddress.Address -band $SubnetMask.Address) -eq ($NetworkId.Address -band $SubnetMask.Address)) {
|
||||
Write-Warning "DNS record already exists, CIDR Block match"
|
||||
}
|
||||
Else {
|
||||
Write-Error "DNS record already exists, and does not match CIDR Block"
|
||||
Break
|
||||
}
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Pre-existing IP address not found, creating new DNS record."
|
||||
New-ITDIbDNSRecordNextAvailableIP -Hostname $FQDN -CIDR $CIDR -Credential $RadiusCred
|
||||
Start-Sleep -Seconds 5
|
||||
Write-Verbose -Message ("FQDN is " + $FQDN)
|
||||
#######
|
||||
####### Remove 10.10.10.10 references when DNS sync is fixed
|
||||
#######
|
||||
#[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction Stop -Server 10.10.10.10).IPAddress
|
||||
|
||||
[Net.IPAddress]$IpAddress = (Get-ITDIbDNSRecord -Hostname $FQDN -Credential $RadiusCred).IPv4Address
|
||||
|
||||
If ((Test-NetConnection -ComputerName $IpAddress.IPAddressToString).PingSucceeded) {
|
||||
Write-Error "IP Address already in use." -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Passwordstate: If local administrator password does not exist in vault, create it."
|
||||
If ($FQDN -like "itdcnd*") {
|
||||
$PasswordStateList = "Peoplesoft Share PW"
|
||||
}
|
||||
Else {
|
||||
$PasswordStateList = "CSRC"
|
||||
}
|
||||
|
||||
$GuestVMLocalCredential = Get-ITDPassword -Title $FQDN -UserName itdadmin -Credential $Credential
|
||||
If ($null -eq $GuestVMLocalCredential) {
|
||||
Write-Verbose -Message "Passwordstate: Local admin password record does not exist, creating new credentials"
|
||||
$GuestVMLocalCredential = New-ITDPassword -Title $FQDN -UserName itdadmin -Description 'Local Administrator' -PasswordList $PasswordStateList -Credential $Credential
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Passwordstate: Local admin password record already exists, use those credentials"
|
||||
}
|
||||
$GuestCredentialAB = New-Object System.Management.Automation.PSCredential ('itdadmin', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
$GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
|
||||
Write-Verbose -Message "Determine environment"
|
||||
switch ($VMEnvironment) {
|
||||
{ $_ -eq 'Test' -or $_ -eq 'Development' } {
|
||||
Write-Verbose -Message "Environment is Test or Development"
|
||||
$EnvShortString = 'tst'
|
||||
}
|
||||
'Production' {
|
||||
Write-Verbose -Message "Environment is Production"
|
||||
$EnvShortString = 'prd'
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Determine subscription, via Subnet"
|
||||
$AllSubscriptions = Get-AzSubscription
|
||||
$AllAzVirtualNetworks = ForEach ($Subscription in $AllSubscriptions) {
|
||||
Set-AzContext -SubscriptionObject $Subscription | Out-Null
|
||||
Get-AzVirtualNetwork | ForEach-Object {
|
||||
[PSCustomObject]@{
|
||||
Subscription = $Subscription.Name;
|
||||
VirtualNetwork = $_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Subscription = ($AllAzVirtualNetworks | Where-Object { $_.VirtualNetwork.Subnets.AddressPrefix -match $Subnet }).Subscription
|
||||
$VirtualNetwork = ($AllAzVirtualNetworks | Where-Object { $_.VirtualNetwork.Subnets.AddressPrefix -match $Subnet }).VirtualNetwork
|
||||
$VirtualNetworkSubnet = $VirtualNetwork.Subnets | Where-Object { $_.AddressPrefix -match $Subnet }
|
||||
|
||||
$VNetName = $VirtualNetwork.Name
|
||||
$VNetSubnet = $VirtualNetworkSubnet.Name
|
||||
|
||||
Write-Verbose -Message "OS is $OS"
|
||||
switch ($OS) {
|
||||
{ $_ -eq "Windows Server 2019 Datacenter" -or $_ -eq "Windows Server 2019" } {
|
||||
Write-Verbose -Message "OS is Windows Server 2019"
|
||||
$VMOS = "Windows"
|
||||
$Publisher = "MicrosoftWindowsServer"
|
||||
$Offer = "WindowsServer"
|
||||
$sku = "2019-Datacenter"
|
||||
$SecurityType = ''
|
||||
}
|
||||
{$_ -eq "Windows Server 2022 Datacenter" -or $_ -eq "Windows Server 2022" } {
|
||||
Write-Verbose -Message "OS is Windows Server 2022"
|
||||
$VMOS = "Windows"
|
||||
$Publisher = "MicrosoftWindowsServer"
|
||||
$Offer = "windowsserver2022"
|
||||
$sku = "2022-datacenter"
|
||||
$SecurityType = ''
|
||||
}
|
||||
{ $_ -eq "Windows Server 2025 Datacenter" -or $_ -eq "Windows Server 2025" } {
|
||||
Write-Verbose -Message "OS is Windows Server 2025"
|
||||
$VMOS = "Windows"
|
||||
$Publisher = "MicrosoftWindowsServer"
|
||||
$Offer = "WindowsServer"
|
||||
$sku = "2025-datacenter"
|
||||
$SecurityType = ''
|
||||
}
|
||||
"Windows 11 24H2" {
|
||||
Write-Verbose -Message "OS is Windows 11 24H2"
|
||||
$VMOS = "Windows"
|
||||
$Publisher = "MicrosoftWindowsDesktop"
|
||||
$Offer = "Windows-11"
|
||||
$sku = "win11-24h2-ent"
|
||||
$SecurityType = ''
|
||||
}
|
||||
Default { Write-Error "Invalid operating system" -ErrorAction Stop }
|
||||
}
|
||||
|
||||
# finalize VM location and size
|
||||
$location = "centralus"
|
||||
|
||||
switch ($PSCmdLet.ParameterSetName) {
|
||||
"VmSizeOverride" {
|
||||
Write-Verbose -Message "ParameterSet is VmSizeOverride"
|
||||
$VMSize = Get-AzVMSize -Location centralus | Where-Object { $_.Name -eq $VMSizeOverride }
|
||||
}
|
||||
"VmSizeDetermine" {
|
||||
Write-Verbose -Message "ParameterSet is VmSizeDetermine"
|
||||
$VMSizeFilter1 = @(
|
||||
"Standard_D2ds_v5",
|
||||
"Standard_D4ds_v5",
|
||||
"Standard_D8ds_v5",
|
||||
"Standard_D16ds_v5",
|
||||
"Standard_D32ds_v5",
|
||||
"Standard_E2ds_v5",
|
||||
"Standard_E4ds_v5",
|
||||
"Standard_E8ds_v5",
|
||||
"Standard_E16ds_v5",
|
||||
"Standard_E20ds_v5",
|
||||
"Standard_E32ds_v5",
|
||||
"Standard_F2s_v2",
|
||||
"Standard_F4s_v2",
|
||||
"Standard_F8s_v2",
|
||||
"Standard_F16s_v2",
|
||||
"Standard_F32s_v2"
|
||||
)
|
||||
|
||||
$VMSize = Get-AzVMSize -Location centralus | `
|
||||
Where-Object { $VMSizeFilter1 -contains $_.Name } | `
|
||||
Where-Object { $_.NumberOfCores -ge $CPU -and $_.MemoryInMB -ge ($Memory * 1024) } | `
|
||||
Where-Object Name -NotMatch "_Promo" | `
|
||||
Sort-Object NumberOfCores, MemoryInMB | `
|
||||
Select-Object -First 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Determine ResourceGroupName, and create Resource Group if needed"
|
||||
If ($PSBoundParameters.ContainsKey('ResourceGroupNameOverride')) {
|
||||
# use the name in the variable
|
||||
Write-Verbose -Message "ResourceGroupName parameter found, is $ResourceGroupNameOverride"
|
||||
$ResourceGroupName = $ResourceGroupNameOverride
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "ResourceGroupName parameter not found, determine now"
|
||||
# if name is not given, determine the name
|
||||
$VMOwner = $AppName.split('-')[0].ToLower()
|
||||
$VMFunction = (($AppName -replace "$VMOwner-") -replace "-").ToLower() -replace " "
|
||||
$ResourceGroupName = "rg-$VMOwner-$VMFunction-$EnvShortString"
|
||||
}
|
||||
Write-Verbose -Message "ResourceGroupName is $ResourceGroupName"
|
||||
|
||||
Write-Verbose -Message "Change to selected subscription and validate resource group exists"
|
||||
Set-AzContext $Subscription | Out-Null
|
||||
$ResGroupExist = $null
|
||||
$ResGroupExist = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
|
||||
|
||||
If (@($ResGroupExist).count -gt 1) {
|
||||
Write-Error "Multiple Resource Groups matched" -ErrorAction Stop
|
||||
}
|
||||
If (@($ResGroupExist).count -lt 1) {
|
||||
Write-Warning "No matching resource group found, creating it now"
|
||||
New-AzResourceGroup -Name $ResourceGroupName -Location $Location -Tag @{ApplicationName = $AppName }
|
||||
}
|
||||
If (@($ResGroupExist).count -eq 1) {
|
||||
Write-Verbose -Message "Exactly one matching resource group found"
|
||||
}
|
||||
|
||||
$VMName = "vm-$HostName-$EnvShortString"
|
||||
$VMIPConfigName = "ipconfig-$HostName-$EnvShortString"
|
||||
$VMNicName = "nic-$HostName-$EnvShortString"
|
||||
$VMObjectName = "vm-$HostName-$EnvShortString"
|
||||
$VMOsDiskName = "vm-$HostName-os-$EnvShortString"
|
||||
|
||||
Write-Verbose -Message "Verifying Applicable Inputs are Lowercase"
|
||||
$VMIPConfigName = $VMIPConfigName.ToLower()
|
||||
$VMNicName = $VMNicName.ToLower()
|
||||
$VMObjectName = $VMObjectName.ToLower()
|
||||
$VMOsDiskName = $VMOSDiskName.ToLower()
|
||||
|
||||
Write-Verbose -Message "Creating IPConfig and NIC"
|
||||
$IPConfig = New-AzNetworkInterfaceIpConfig -Name $VMIPConfigName -PrivateIpAddress $IPAddress.IPAddressToString -PrivateIpAddressVersion IPv4 -Subnet $VirtualNetworkSubnet
|
||||
$VMNIC = New-AzNetworkInterface -IpConfigurationName $IPConfig -Location $Location -Name $VMNICName -ResourceGroupName $ResourceGroupName -Subnet $VirtualNetworkSubnet -Force
|
||||
$VMNIC.IpConfigurations[0].PrivateIpAllocationMethod = "Static"
|
||||
$VMNIC.IpConfigurations[0].PrivateIpAddress = $IPAddress.IPAddressToString
|
||||
Set-AzNetworkInterface -NetworkInterface $VMNIC
|
||||
|
||||
Write-Verbose -Message "Build VM Config"
|
||||
$vmConfigParams = @{
|
||||
VMName = $VMObjectName
|
||||
VMSize = $VMSize.Name
|
||||
}
|
||||
|
||||
If ($PSBoundParameters.ContainsKey('AvailabilityZone')) {
|
||||
Write-Verbose -Message "AvailabilityZone parameter found, adding to vmConfigParams"
|
||||
$vmConfigParams += @{
|
||||
Zone = $AvailabilityZone
|
||||
}
|
||||
}
|
||||
|
||||
$vmConfig = New-AzVMConfig @vmConfigParams
|
||||
|
||||
# Security Profile
|
||||
switch ($SecurityType) {
|
||||
'TrustedLaunch' {
|
||||
Write-Verbose -Message "Security Profile is TrustedLaunch"
|
||||
$vmConfig = Set-AzVMSecurityProfile -VM $vmConfig -SecurityType "TrustedLaunch"
|
||||
}
|
||||
'' {
|
||||
Write-Verbose -Message "Security Profile is empty"
|
||||
}
|
||||
Default {
|
||||
Write-Verbose -Message "Security Profile is null"
|
||||
}
|
||||
}
|
||||
|
||||
$vmConfig | Set-AzVMOSDisk -Name $VMOSDiskName -CreateOption FromImage
|
||||
If ($VMOS -eq "Windows") {
|
||||
$vmConfig | Set-AzVMOperatingSystem -Windows -ComputerName $VMName -Credential $GuestVMLocalCredential
|
||||
$vmConfig | Set-AzVMSourceImage -PublisherName $Publisher -Offer $Offer -Skus $Sku -Version latest
|
||||
$vmConfig.OSProfile.ComputerName = $HostName
|
||||
}
|
||||
If ($VMOS -eq "Linux") {
|
||||
$vmConfig | Set-AzVMOperatingSystem -Linux -ComputerName $VMFQDN -Credential $GuestVMLocalCredential
|
||||
Switch ($VMSubscription) {
|
||||
"npd01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/76297098-764c-43de-8525-c9fda1b237be/resourceGroups/rg-infra-templates-tst-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-103" }
|
||||
"infra01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/e53aa0c7-824d-40a2-b420-4ab77b1051d2/resourceGroups/rg-infra-templates-prd-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-403" }
|
||||
"prd01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/437b2bfa-850e-4464-b6c2-38a68cda7c69/resourceGroups/rg-infra-templates-prd-002/providers/Microsoft.Compute/images/vm-rhel74template-prd-003" }
|
||||
}
|
||||
}
|
||||
|
||||
$vmConfig | Add-AzVMNetworkInterface -Id $VMNIC.ID
|
||||
Set-AzVMBootDiagnostic -VM $vmConfig -Enable -ResourceGroupName $ResourceGroupName
|
||||
|
||||
Write-Verbose "Creating VM"
|
||||
New-AzVM -VM $vmConfig -ResourceGroupName $ResourceGroupName -Location $Location -DisableBginfoExtension -LicenseType "Windows_Server" #-AsJob
|
||||
|
||||
Write-Verbose -Message "New-AzVM completed, waiting 60 seconds before checking for completion"
|
||||
Start-Sleep -Seconds 60
|
||||
|
||||
$VM = Get-AzVM -Name $VMObjectName -ResourceGroupName $ResourceGroupName
|
||||
|
||||
If ($PSBoundParameters.ContainsKey("DiskDataGB")) {
|
||||
$ExistingDisks = @($VM.StorageProfile.DataDisks | Select-Object *, @{n = 'ItdId'; e = { [int]($_.Name -replace "vm-$hostname-app-$VMEnvironment-") } })
|
||||
|
||||
$NewDiskItdIdInt = ($ExistingDisks | Sort-Object ItdId -Descending | Select-Object -First 1).ItdId + 1
|
||||
$NewDiskItdIdStr = $NewDiskItdIdInt.ToString("000")
|
||||
$NewDiskName = "vm-$Hostname-app-$EnvShortString-$NewDiskItdIdStr" #vm-itduc4p1-app-tst-001
|
||||
|
||||
$LunID = ($ExistingDisks | Sort-Object Lun -Descending | Select-Object -First 1).Lun + 1
|
||||
|
||||
$count = 0
|
||||
|
||||
If ($ExistingDisks) {
|
||||
while ($Size -match $ExistingDisks.DiskSizeGB) {
|
||||
$count++
|
||||
Write-Verbose -Message "DiskDataGB: $DiskDataGB, Count: $count"
|
||||
If ($count -ge 11) {
|
||||
Write-Error "Disk size not available" -ErrorAction Stop
|
||||
}
|
||||
Else {
|
||||
$Size = $Size - 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "DiskDataGB: $DiskDataGB, Count: $count"
|
||||
|
||||
If ($null -ne $DiskDataGB -and $DiskDataGB -gt 0) {
|
||||
$AzureRmDiskConfigParams = @{
|
||||
DiskSizeGB = $DiskDataGB
|
||||
Location = $Location
|
||||
CreateOption = "Empty"
|
||||
SkuName = "Premium_LRS"
|
||||
}
|
||||
|
||||
If ($AvailabilityZone) {
|
||||
Write-Verbose "VM is located in Availability Zone $Zone"
|
||||
$AzureRmDiskConfigParams += @{Zone = $Zone }
|
||||
}
|
||||
#$DiskConfig = New-AzureRmDiskConfig -DiskSizeGB $Size -Location $Location -CreateOption Empty -SkuName Premium_LRS
|
||||
$DiskConfig = New-AzDiskConfig @AzureRmDiskConfigParams
|
||||
|
||||
If (!(Get-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $NewDiskName -ErrorAction SilentlyContinue)) {
|
||||
$NewDisk = New-AzDisk -DiskName $NewDiskName -Disk $DiskConfig -ResourceGroupName $ResourceGroupName
|
||||
|
||||
$VM = Add-AzVMDataDisk -Name $NewDiskName -CreateOption Attach -ManagedDiskId $NewDisk.Id -VM $VM -Lun $LunID -Caching ReadOnly
|
||||
Update-AzVM -VM $VM -ResourceGroupName $ResourceGroupName -AsJob
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Add networking tag to VM" -Verbose
|
||||
$Tags = @{"Network"="StandardWindows"}
|
||||
New-AzTag -ResourceId $VM.Id -Tag $Tags
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
function New-ITDWindowsVMAzureStep2 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$FQDN,
|
||||
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[string]
|
||||
$VMEnvironment,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
$search = @()
|
||||
#$Hostname = $VMName.split('-')[1]
|
||||
$HostName = $FQDN.split('.')[0]
|
||||
###$VMEnvironment = $VMName.split('-')[2] ####
|
||||
|
||||
Write-Verbose -Message ("Searching for Az VM name like " + $HostName)
|
||||
Get-AzSubscription | ForEach-Object {
|
||||
$Subscription = $_
|
||||
Set-AzContext $_ | Out-Null
|
||||
$search += Get-AzVM | Where-Object Name -Like "*$HostName*" | Select-Object *, @{n = 'SubscriptionName'; e = { $Subscription.Name } }
|
||||
}
|
||||
}
|
||||
|
||||
process {
|
||||
switch (@($Search).count) {
|
||||
{ $_ -gt 1 } { Write-Error -Message "More than one virtual machine match found." }
|
||||
{ $_ -lt 1 } { Write-Error -Message "Less than one virtual machine match found." }
|
||||
{ $_ -eq 1 } {
|
||||
Write-Verbose -Message "Prep VM variables"
|
||||
$VMName = $search.Name
|
||||
$ResourceGroupName = $search.ResourceGroupName
|
||||
#$VMEnvironment = $VMName.split('-')[2]
|
||||
$SubscriptionName = $search.SubscriptionName
|
||||
Set-AzContext -Subscription $SubscriptionName
|
||||
|
||||
$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName
|
||||
|
||||
Write-Verbose -Message "Set firewall tag"
|
||||
$Tags = @{"Network"="StandardWindows"}
|
||||
New-AzTag -ResourceId $VM.Id -Tag $Tags
|
||||
|
||||
Write-Verbose -Message "Wait two minutes before Pre-Firewall Guest OS customization"
|
||||
#Start-Sleep -Seconds 120
|
||||
|
||||
Write-Verbose -Message "Begin Pre-Firewall Guest OS customization"
|
||||
$InvokeAzVMRunCommandParams = @{
|
||||
ResourceGroupName = $ResourceGroupName;
|
||||
Name = $VMName;
|
||||
CommandId = 'RunPowerShellScript'
|
||||
}
|
||||
|
||||
Write-Verbose -Message "1-Set WMI Tags"
|
||||
$ScriptBlock = {
|
||||
try {
|
||||
Write-Verbose "Create new Class"
|
||||
$Class = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
|
||||
|
||||
$Class["__CLASS"] = "ITD";
|
||||
$Class.Qualifiers.Add("Static", $true)
|
||||
$Class.Properties.Add("MyKey", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties["MyKey"].Qualifiers.Add("Key", $true)
|
||||
|
||||
$Class.Properties.Add("LastModified", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties.Add("DTAP", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties.Add("Baseline", [System.Management.CimType]::String, $false)
|
||||
|
||||
$Class.Put()
|
||||
|
||||
Write-Verbose "Create single ITD Object"
|
||||
Set-WmiInstance -Class ITD -Arguments @{LastModified = (Get-Date); DTAP = "Prod"; Baseline = "000" }
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "3-Disk Configuration"
|
||||
$ScriptBlock = {
|
||||
try {
|
||||
# Non-initialized and MBR-initialized disks will have 0 partitions by default, but GPT-initialized disks will have 1 system reserved partition by default
|
||||
$disks = Get-Disk | Where-Object { $_.NumberOfPartitions -eq 0 -or ( $_.PartitionStyle -eq 'GPT' -and $_.NumberOfPartitions -eq 1 ) } | Sort-Object -Property Number
|
||||
|
||||
if ($disks) {
|
||||
Write-Verbose -Message "Found $(@($disks).Count) unpartitioned disks."
|
||||
|
||||
# Prevent the "You must format this partition before using it." popup
|
||||
if (Get-Service ShellHWDetection -ErrorAction SilentlyContinue) { Stop-Service ShellHWDetection -ErrorAction SilentlyContinue }
|
||||
|
||||
foreach ($disk in $disks) {
|
||||
if ($disk.IsOffline) {
|
||||
Set-Disk $disk.Number -IsOffline $false
|
||||
Write-Verbose -Message "Brought disk $($disk.Number)($("{0:n0}GB" -f ($disk.Size / 1GB))) online..."
|
||||
}
|
||||
|
||||
if ($disk.IsReadOnly) { Set-Disk $disk.Number -IsReadOnly $false }
|
||||
if ($disk.PartitionStyle -eq 'RAW') { Initialize-Disk $disk.Number -PartitionStyle GPT -ErrorAction SilentlyContinue }
|
||||
|
||||
$diskParam = @{
|
||||
FileSystem = 'NTFS'
|
||||
Confirm = $false
|
||||
}
|
||||
|
||||
$driveLetter = [Int][Char]'D'
|
||||
while (Get-Volume -DriveLetter $([Char]$driveLetter) -ErrorAction SilentlyContinue) {
|
||||
$driveLetter++
|
||||
}
|
||||
|
||||
$diskParam.DriveLetter = [Char]$driveLetter
|
||||
|
||||
if (@($disks).IndexOf($disk) -eq 0 -and (-not (Get-Volume -DriveLetter D -ErrorAction SilentlyContinue))) {
|
||||
$diskParam.NewFileSystemLabel = 'Temporary Storage'
|
||||
}
|
||||
elseif (@($disks).IndexOf($disk) -eq 1 -and (-not (Get-Volume -DriveLetter E -ErrorAction SilentlyContinue))) {
|
||||
$diskParam.NewFileSystemLabel = 'Data'
|
||||
}
|
||||
|
||||
[void](New-Partition -DiskNumber $disk.Number -DriveLetter $diskParam.DriveLetter -UseMaximumSize)
|
||||
[void](Format-Volume @diskParam)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "No unpartitioned disks found, continuing..."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
finally {
|
||||
if (Get-Service ShellHWDetection -ErrorAction SilentlyContinue) { Start-Service ShellHWDetection -ErrorAction SilentlyContinue }
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "5-Time Zone"
|
||||
$ScriptBlock = {
|
||||
if ((Get-TimeZone).Id -ne 'Central Standard Time') {
|
||||
Write-Verbose -Message "Current time zone set to $((Get-TimeZone).Id), setting to Central Standard Time."
|
||||
|
||||
Set-TimeZone -Id 'Central Standard Time'
|
||||
|
||||
Write-Verbose -Message "Time zone set to Central Standard Time."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Time zone is already set to Central Standard Time."
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "6-Enable Performance Counters"
|
||||
$ScriptBlock = {
|
||||
# Enable Performance Counters
|
||||
try {
|
||||
if (Get-ScheduledTask -TaskName "Server Manager Performance Monitor" | Where-Object State -NE "Running" -ErrorAction SilentlyContinue) {
|
||||
Enable-ScheduledTask -TaskPath "\Microsoft\Windows\PLA\" -TaskName "Server Manager Performance Monitor" | Start-ScheduledTask
|
||||
|
||||
Write-Verbose -Message "Performance monitors enabled."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Performance monitors already enabled, continuing..."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
Write-Verbose -Message "7-Disable Windows Firewall"
|
||||
$ScriptBlock = {
|
||||
# Disable Windows Firewall
|
||||
Write-Verbose -Message "Checking for active Windows Firewall..."
|
||||
|
||||
if ((Get-NetFirewallProfile).Enabled -contains 'True') {
|
||||
Write-Verbose -Message "Windows Firewall is still enabled, disabling it..."
|
||||
|
||||
Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
|
||||
|
||||
Write-Verbose -Message "Windows Firewall disabled."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Windows Firewall already disabled, continuing..."
|
||||
}
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock
|
||||
|
||||
# determine Active Directory Forest and OU
|
||||
Write-Verbose -Message "8a-Determine domain join"
|
||||
$DomainName = $FQDN.Substring($FQDN.IndexOf(".") + 1)
|
||||
|
||||
switch ($DomainName) {
|
||||
'nd.gov' {
|
||||
$SearchBaseDomain = "dc=nd,dc=gov"
|
||||
}
|
||||
'ndcloud.gov' {
|
||||
$SearchBaseDomain = "dc=ndcloud,dc=gov"
|
||||
}
|
||||
}
|
||||
|
||||
If ($DomainName -eq "nd.gov") {
|
||||
$OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq $AppName }
|
||||
If ($null -eq $OUAppName) {
|
||||
Write-Verbose -Message "Dedicated AppName OU for $AppName not found, placing in All-General OU"
|
||||
$OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq 'All-General' }
|
||||
}
|
||||
Write-Verbose -Message ("OUAppName: " + $OUAppName.distinguishedName) -Verbose
|
||||
|
||||
# Determine if the Environment sub-ou is special for the selected AppName
|
||||
switch ($OUAppName.Name) {
|
||||
'Shared-PeopleSoft-HigherEd' {
|
||||
switch ($VMEnvironment) {
|
||||
'Test' { $EnvString = "Non-Prod" }
|
||||
'Production' { $EnvString = "Prod" }
|
||||
}
|
||||
}
|
||||
'Shared-PeopleSoft-State' {
|
||||
switch ($VMEnvironment) {
|
||||
'Test' { $EnvString = "Non-Prod" }
|
||||
'Production' { $EnvString = "Prod" }
|
||||
}
|
||||
}
|
||||
Default {
|
||||
switch ($VMEnvironment) {
|
||||
'Test' { $EnvString = "Test" }
|
||||
'Production' { $EnvString = "Prod" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Gather all sub OUs for the AppName OU
|
||||
Write-Verbose -Message "EnvString is $EnvString" -Verbose
|
||||
$SubOUs = Get-ADOrganizationalUnit -Server $DomainName -SearchBase $OUAppName -Filter *
|
||||
Write-Verbose -Message ("SubOUs:") -Verbose
|
||||
$SubOUs | ForEach-Object { Write-Verbose -Message $_.DistinguishedName -Verbose }
|
||||
$LikeFilter = ("OU=$EnvString,OU=" + $OUAppName.Name + "*")
|
||||
Write-Verbose -Message "LikeFilter: $LikeFilter"
|
||||
$OUToUse = $SubOUs | Where-Object { $_.DistinguishedName -like $LikeFilter }
|
||||
Write-Verbose -Message ("OUToUse: " + $OUToUse.distinguishedName) -Verbose
|
||||
|
||||
<#$MatchFilter = ("OU=$EnvString,OU=" + $OUAppName.Name)
|
||||
Write-Verbose -Message "MatchFiler: $MatchFilter"
|
||||
$OutToUseMatch = ($SubOUs | Where-Object {$_.DistinguishedName -match "^$MatchFilter"})
|
||||
Write-Verbose -Message ("OUToUseMatch: " + $OUToUseMatch.distinguishedName) -Verbose
|
||||
#>
|
||||
# Determine if AD Computer object already exists
|
||||
$ExistingADComputer = Get-ADComputer -Filter { Name -eq $HostName }
|
||||
If ($ExistingADComputer) {
|
||||
Write-Verbose -Message "AD Object already exists in AD"
|
||||
# Determine if AD Computer object is in the correct OU, or a sub-OU of the correct OU
|
||||
If ($ExistingADComputer.DistinguishedName -like ("*" + $OUToUse.DistinguishedName)) {
|
||||
Write-Verbose -Message "AD Object is in the correct OU, will attempt domain join"
|
||||
$AttemptDomainJoin = $true
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "AD Object is NOT in the correct OU, domain join will not occur, needs human review"
|
||||
$AttemptDomainJoin = $false
|
||||
}
|
||||
}
|
||||
Else {
|
||||
$AttemptDomainJoin = $true
|
||||
}
|
||||
|
||||
If ($AttemptDomainJoin) {
|
||||
$OuFinal = $OUToUse.DistinguishedName
|
||||
Write-Verbose -Message "OuFinal: $OuFinal" -Verbose
|
||||
|
||||
$FirstScriptBlock = { $DomainJoinCred = New-Object System.Management.Automation.PSCredential('svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force)) }
|
||||
$SecondScriptText = 'Add-Computer -DomainName <DomainName> -OUPath "<OuPath>" -Credential $DomainJoinCred -Server itddc42.nd.gov -Verbose; Restart-Computer -Force -Verbose;'
|
||||
$SecondScriptText = $SecondScriptText -replace '<DomainName>', $DomainName
|
||||
$SecondScriptText = $SecondScriptText -replace '<OuPath>', $OuFinal
|
||||
|
||||
#Write-Verbose -Message "[$FQDN]:Invoke-VMScript to AD join"
|
||||
$InvokeVMScriptFunc = [System.Management.Automation.ScriptBlock]::Create("$FirstScriptBlock ; $SecondScriptText")
|
||||
}
|
||||
Write-Verbose -Message $InvokeVMScriptFunc.tostring() -Verbose ###
|
||||
<# wait for firewall and reconnect
|
||||
Write-Verbose -Message "8b-Verify if firewall is open before domain join attempt"
|
||||
$connectivity = $false
|
||||
while ($connectivity -eq $false) {
|
||||
If ( (Test-NetConnection -ComputerName $FQDN).PingSuccedded) {
|
||||
Write-Verbose -Message "Ping successful" -Verbose
|
||||
$connectivity = $true
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Ping failed, may need to wait for PA IP sync" -Verbose
|
||||
Start-Sleep -Seconds 60
|
||||
}
|
||||
}
|
||||
#>
|
||||
Write-Verbose -Message "8c-Attempt domain join"
|
||||
switch ($DomainName) {
|
||||
'nd.gov' {
|
||||
Write-Verbose -Message "Attempting domain join nd.gov"
|
||||
Write-Verbose -Message "Domain: $DomainName"
|
||||
Write-Verbose -Message "OuPath: $OuFinal"
|
||||
<#Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptPath 'C:\AzWinBuild\8-Domain-ndgov.ps1' -Parameter @{
|
||||
"DomainName" = 'nd.gov';
|
||||
"OuPath" = "$OuFinal"
|
||||
}#>
|
||||
$ScriptBlock = {
|
||||
Param(
|
||||
[string]
|
||||
$DomainName,
|
||||
|
||||
[string]
|
||||
$OuPath
|
||||
)
|
||||
|
||||
Write-Host $OuPath
|
||||
Test-NetConnection -ComputerName nd.gov
|
||||
|
||||
$DomainJoinCred = New-Object System.Management.Automation.PSCredential('ndgov\svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force))
|
||||
Add-Computer -DomainName $DomainName -OUPath $OuPath -Credential $DomainJoinCred
|
||||
|
||||
Restart-Computer -Force
|
||||
}
|
||||
Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock -Parameter @{
|
||||
"DomainName" = 'nd.gov';
|
||||
"OuPath" = "$OuFinal"
|
||||
}
|
||||
#Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $InvokeVMScriptFunc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+529
@@ -0,0 +1,529 @@
|
||||
<#
|
||||
### step 1
|
||||
gather specs
|
||||
provision virtual machine on hypervisor
|
||||
|
||||
final result:
|
||||
cpu configured, with hot-add
|
||||
memory configured, with hot-add
|
||||
disks 1, 2, and 3 attached with correct sizing
|
||||
network connected
|
||||
virtual machine powered on
|
||||
|
||||
update SCTASK Step 1 completed
|
||||
|
||||
### step 2 - customize Windows Guest OS
|
||||
wait for SCTASK Step 1 completed message
|
||||
configure
|
||||
time zone, mount points, page file, etc
|
||||
|
||||
update SCTASK Step 2 completed
|
||||
|
||||
### step 3 - administration
|
||||
wait for SCTASK Step 2 completed message
|
||||
domain join
|
||||
nd.gov at a minimum
|
||||
install sccm
|
||||
regardless if domain joined
|
||||
|
||||
update SCTASK Step 3 completed
|
||||
|
||||
### step 4 - validation
|
||||
|
||||
|
||||
confirm all agents are installed
|
||||
disks are present and formatted
|
||||
|
||||
#>
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
$NewITDWindowsVmVMwareParams = @{
|
||||
ComputerName = 'itdzmtest300.nd.gov';
|
||||
CPU = 2;
|
||||
MemoryGB = 8;
|
||||
DiskOsGB = 50;
|
||||
DiskSwapGB = 9;
|
||||
DiskDataGB = 20;
|
||||
Subnet = '10.11.12.0/23';
|
||||
OS = 'Windows Server 2022 Datacenter';
|
||||
Environment = "Test";
|
||||
Datacenter = "Bismarck";
|
||||
AppName = "ITD-POC-zmeier";
|
||||
StartupPriority = 4;
|
||||
LicensingRestrictions = "No Licensing Restrictions";
|
||||
Credential = $PrvCred;
|
||||
}
|
||||
|
||||
New-ITDWindowsVmVMware @NewITDWindowsVmVMwareParams
|
||||
#>
|
||||
|
||||
function New-ITDWindowsVmVMwareStep1 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$FQDN,
|
||||
|
||||
[int]
|
||||
$CPU,
|
||||
|
||||
[int]
|
||||
$MemoryGB,
|
||||
|
||||
[int]
|
||||
$DiskOsGB,
|
||||
|
||||
[int]
|
||||
$DiskSwapGB,
|
||||
|
||||
[int]
|
||||
$DiskDataGB,
|
||||
|
||||
[string]
|
||||
$Subnet,
|
||||
|
||||
[string]
|
||||
$OS,
|
||||
|
||||
[string]
|
||||
$VMEnvironment,
|
||||
|
||||
[string]
|
||||
$Datacenter,
|
||||
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[int]
|
||||
$StartupPriority,
|
||||
|
||||
[string]
|
||||
$LicensingRestrictions,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
$FQDN = $FQDN.ToLower()
|
||||
$HostName = $FQDN.split('.')[0]
|
||||
|
||||
If ($VMEnvironment -eq "Development") {
|
||||
$VMEnvironment = "Test"
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Prepare Credentials and Connections"
|
||||
$RadiusCred = New-Object System.Management.Automation.PSCredential($Credential.username.split('\')[1], ($Credential.Password))
|
||||
|
||||
Write-Verbose -Message "Infoblox: Find DNS pre-existing record, or create one"
|
||||
Write-Verbose -Message "FQDN: $FQDN" -Verbose
|
||||
Write-Verbose -Message "Hostname: $Hostname" -Verbose
|
||||
|
||||
Clear-DnsClientCache
|
||||
$InfobloxVlanMetadata = Get-ITDIbVlan -CIDR $Subnet -Credential $RadiusCred
|
||||
$Cidr = ($InfobloxVlanMetadata.AssignedTo | Out-String).TrimEnd()
|
||||
[Net.IpAddress]$NetworkId = $Cidr.split('/')[0]
|
||||
#######
|
||||
####### Remove 10.10.10.10 references when DNS sync is fixed
|
||||
#######
|
||||
[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction SilentlyContinue -Server 10.10.10.10).IPAddress
|
||||
$SubnetMaskInt = $CIDR.split('/')[1]
|
||||
$Int64 = ([convert]::ToInt64(('1' * $SubnetMaskInt + '0' * (32 - $SubnetMaskInt)), 2))
|
||||
[Net.IPAddress]$SubnetMask = '{0}.{1}.{2}.{3}' -f ([math]::Truncate($Int64 / 16777216)).ToString(),
|
||||
([math]::Truncate(($Int64 % 16777216) / 65536)).ToString(),
|
||||
([math]::Truncate(($Int64 % 65536) / 256)).ToString(),
|
||||
([math]::Truncate($Int64 % 256)).ToString()
|
||||
$IPSplit = $Subnet.Split('.')
|
||||
[Net.IPAddress]$DefaultGateway = ($IPSplit[0] + '.' + $IPSplit[1] + '.' + $IPSplit[2] + '.' + (($CIDR.split('/')[0].split('.')[-1] -as [int]) + 1) )
|
||||
|
||||
If ($null -ne $IpAddress) {
|
||||
If (($IpAddress.Address -band $SubnetMask.Address) -eq ($NetworkId.Address -band $SubnetMask.Address)) {
|
||||
Write-Verbose -Message "DNS record already exists, CIDR Block match"
|
||||
}
|
||||
Else {
|
||||
Write-Error "DNS record already exists, but does not match CIDR Block"
|
||||
Break
|
||||
}
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Pre-existing IP address not found, creating new DNS record."
|
||||
try {
|
||||
New-ITDIbDNSRecordNextAvailableIP -Hostname $FQDN -CIDR $CIDR -Credential $RadiusCred
|
||||
Start-Sleep -Seconds 5
|
||||
Write-Verbose -Message ("FQDN is " + $FQDN)
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#######
|
||||
####### Review this code after DNS problems resolved - 2024/09/24 zm
|
||||
#######
|
||||
####### [Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction Stop -Server 10.10.10.10).IPAddress
|
||||
|
||||
[Net.IPAddress]$IpAddress = (Get-ITDIbDNSRecord -Hostname $FQDN -Credential $RadiusCred).IPv4Address
|
||||
|
||||
If ((Test-NetConnection -ComputerName $IpAddress.IPAddressToString).PingSucceeded) {
|
||||
Write-Error "IP Address already in use." -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Passwordstate: If local administrator password does not exist in vault, create it."
|
||||
If ($FQDN -like "itdcnd*") {
|
||||
$PasswordStateList = "Peoplesoft Share PW"
|
||||
}
|
||||
Else {
|
||||
$PasswordStateList = "CSRC"
|
||||
}
|
||||
|
||||
$GuestVMLocalCredential = Get-ITDPassword -Title $FQDN -UserName itdadmin -Credential $Credential
|
||||
If ($null -eq $GuestVMLocalCredential) {
|
||||
Write-Verbose -Message "Passwordstate: Local admin password record does not exist, creating new credentials"
|
||||
$GuestVMLocalCredential = New-ITDPassword -Title $FQDN -UserName itdadmin -Description 'Local Administrator' -PasswordList $PasswordStateList -Credential $Credential
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Passwordstate: Local admin password record already exists, use those credentials"
|
||||
}
|
||||
$GuestCredentialAB = New-Object System.Management.Automation.PSCredential ('itdadmin', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
$GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
|
||||
Write-Verbose -Message "Decide VMware Cluster and Licensing"
|
||||
switch ($LicensingRestrictions) {
|
||||
"No Licensing Restrictions" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SharePoint Server" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SharePoint Server (Academic)" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SQL Developer" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SQL MSDN" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SQL Standard" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SQL Standard (Academic)" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SQL Standard (Vendor Provided)" { $ClusterRoot = "WINDOWS" }
|
||||
"Microsoft SQL Enterprise" { $ClusterRoot = "SQLe" }
|
||||
"Microsoft SQL Enterprise (Academic)" { $ClusterRoot = "SQLa" }
|
||||
"IBM Websphere" { $ClusterRoot = "WAS" }
|
||||
"Powerschool" { $ClusterRoot = "PS" }
|
||||
"Pexip" { $ClusterRoot = "TEL" }
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Decide Datacenter"
|
||||
switch ($Datacenter) {
|
||||
"Bismarck" { $ClusterInt = 1 }
|
||||
"Mandan" { $ClusterInt = 2 }
|
||||
}
|
||||
|
||||
$Cluster = $ClusterRoot + $ClusterInt
|
||||
Write-Verbose -Message "VMware Cluster is $Cluster, gathering metadata for $Cluster"
|
||||
|
||||
switch ($Cluster) {
|
||||
"WINDOWS1" {
|
||||
$ViServer = 'itdvmvc1.nd.gov'
|
||||
$ComputeCluster = Get-Cluster WINDOWS1
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||||
|
||||
If ($LicensingRestrictions -like "*SQL*") {
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS1_FS92_SQL"
|
||||
$DiskStorageFormat = 'EagerZeroedThick'
|
||||
}
|
||||
Else {
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS1_FS92_Gen"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
}
|
||||
}
|
||||
"WINDOWS2" {
|
||||
$ViServer = 'itdvmvc2.nd.gov'
|
||||
$ComputeCluster = Get-Cluster WINDOWS2
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
If ($LicensingRestrictions -like "*SQL*") {
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS2_FS92_SQL"
|
||||
$DiskStorageFormat = 'EagerZeroedThick'
|
||||
}
|
||||
Else {
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS2_FS92_Gen"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
}
|
||||
}
|
||||
"SQLa1" {
|
||||
$ViServer = 'itdvmvc1.nd.gov'
|
||||
$ComputeCluster = Get-Cluster SQLa1
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||||
$DiskStorageFormat = 'EagerZeroedThick'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "SQLa1_FS92_Gen"
|
||||
}
|
||||
"SQLa2" {
|
||||
$ViServer = 'itdvmvc2.nd.gov'
|
||||
$ComputeCluster = Get-Cluster SQLa2
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||||
$DiskStorageFormat = 'EagerZeroedThick'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "SQLa2_FS92_Gen"
|
||||
}
|
||||
"SQLe1" {
|
||||
$ViServer = 'itdvmvc1.nd.gov'
|
||||
$ComputeCluster = Get-Cluster SQLe1
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||||
$DiskStorageFormat = 'EagerZeroedThick'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "SQLe1_FS92_Gen"
|
||||
}
|
||||
"SQLe2" {
|
||||
$ViServer = 'itdvmvc2.nd.gov'
|
||||
$ComputeCluster = Get-Cluster SQLe2
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||||
$DiskStorageFormat = 'EagerZeroedThick'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "SQLe2_FS92_Gen"
|
||||
}
|
||||
"WAS1" {
|
||||
$ViServer = 'itdvmvc1.nd.gov'
|
||||
$ComputeCluster = Get-Cluster WAS1
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "WAS1_FS92_Gen"
|
||||
}
|
||||
"WAS2" {
|
||||
$ViServer = 'itdvmvc2.nd.gov'
|
||||
$ComputeCluster = Get-Cluster WAS2
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "WAS2_FS92_Gen"
|
||||
}
|
||||
"PS1" {
|
||||
$ViServer = 'itdvmvc1.nd.gov'
|
||||
$ComputeCluster = Get-Cluster PS1
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "PS1_FS92_Gen"
|
||||
}
|
||||
"PS2" {
|
||||
$ViServer = 'itdvmvc2.nd.gov'
|
||||
$ComputeCluster = Get-Cluster PS2
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "PS2_FS92_Gen"
|
||||
}
|
||||
"TEL1" {
|
||||
$ViServer = 'itdvmvc1.nd.gov'
|
||||
$ComputeCluster = Get-Cluster TEL1
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-TEL1-Data"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "TEL1_FS92_Gen"
|
||||
}
|
||||
"TEL2" {
|
||||
$ViServer = 'itdvmvc2.nd.gov'
|
||||
$ComputeCluster = Get-Cluster TEL2
|
||||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-TEL2-Data"
|
||||
$DiskStorageFormat = 'Thin'
|
||||
$DatastoreCluster = Get-DatastoreCluster -Name "TEL2_FS92_Gen"
|
||||
}
|
||||
Default {
|
||||
Write-Error "Cluster not found" -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Validate entered disk sizes"
|
||||
Write-Verbose -Message "DiskOsGB is $DiskOsGB. Validating its not a stupid number." -Verbose
|
||||
switch ($DiskOsGB) {
|
||||
{ $_ -lt 50 } {
|
||||
Write-Verbose -Message "DiskOsGB is 0 or below. Since an OS Disk is required, defaulting to 50GB" -Verbose
|
||||
$DiskOsGB = 50
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "DiskSwapGB is $DiskSwapGB. Validating its not a stupid number." -Verbose
|
||||
switch ($DiskSwapGB) {
|
||||
{ $_ -le 0 } {
|
||||
Write-Verbose -Message "DiskSwapGB is zero or below. Since an Swap Disk is required, defaulting to `$Memory + 1GB." -Verbose
|
||||
$DiskSwapGB = ($MemoryGB + 1)
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "DiskDataGB is $DiskDataGB. Validating its not a stupid number." -Verbose
|
||||
switch ($DiskDataGB) {
|
||||
{ $_ -le 0 } {
|
||||
Write-Verbose -Message "DiskDataGB is 0. Since an Data Disk is optional, data disk will not be created." -Verbose
|
||||
$DiskDataGB = 0
|
||||
}
|
||||
{ $_ -gt 500 } {
|
||||
Write-Verbose -Message "DiskDataGB is 500GB or more. DiskDataGB will be set to the maximum of 500GB." -Verbose
|
||||
$DiskDataGB = 500
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Determine Datastore / Datastore Cluster"
|
||||
$DiskTotal = $DiskOsGB + $DiskSwapGB + $DiskDataGB
|
||||
If ($DatastoreCluster) {
|
||||
}
|
||||
Else {
|
||||
$DatastoreCluster = Get-DatastoreCluster | Where-Object Name -Like ("*" + $ComputeCluster + "*")
|
||||
}
|
||||
$ClusterDatastoreWithHighestFreeSpaceGB = ($DatastoreCluster | Get-Datastore | Sort-Object FreeSpaceGB -Descending | Select-Object -First 1)
|
||||
If ($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB -gt $DiskTotal) {
|
||||
Write-Warning ("VM DiskTotal " + $DiskTotal + "GB, will fit on " + $ClusterDatastoreWithHighestFreeSpaceGB.Name + " (" + [math]::round($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB, 0) + "GB free)")
|
||||
}
|
||||
else {
|
||||
Write-Warning ("VM DiskTotal " + $DiskTotal + "GB, will not fit on " + $ClusterDatastoreWithHighestFreeSpaceGB.Name + " (" + [math]::round($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB, 0) + "GB free)")
|
||||
Write-Error ("New VM " + $FQDN + " needs " + $DiskTotal + "GB of free space on a single datastore in the " + $DatastoreCluster.Name + " datastore cluster.") -ErrorAction Stop
|
||||
}
|
||||
|
||||
$FolderLocation = $ComputeCluster | Get-Datacenter | Get-Folder -Name "_New Builds"
|
||||
|
||||
Write-Verbose -Message "Determine VMware VM Template for OS: $OS"
|
||||
switch ($OS) {
|
||||
"Windows Server 2012R2 Standard" { $Template = "Windows Server 2012R2 Standard" }
|
||||
"Windows Server 2016 Standard" { $Template = "Windows Server 2016 Standard" }
|
||||
"Windows Server 2019" { $Template = "Windows Server 2019 Standard 1809.19" }
|
||||
"Windows Server 2019 Standard" { $Template = "Windows Server 2019 Standard 1809.19" }
|
||||
"Windows Server 2019 Datacenter" { $Template = "Windows Server 2019 Standard 1809.19" }
|
||||
"Windows Server 2022" { $Template = "Windows Server 2022 Standard 2108.21" }
|
||||
"Windows Server 2022 Datacenter" { $Template = "Windows Server 2022 Standard 2108.21" }
|
||||
"Windows Server 2025" { $Template = "Windows Server 2025 Standard 24H2.6" }
|
||||
Default { Write-Error "Invalid template option: $OS" -ErrorAction Stop }
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Determine VMware Port Group"
|
||||
$PortGroupsAvailable = Get-VDPortgroup -Server $ViServer -VDSwitch $VirtualSwitch
|
||||
$PortGroup = $PortGroupsAvailable | Where-Object Name -Like ("dvPG_*" + $NetworkId.IPAddressToString + "_" + $SubnetMaskInt)
|
||||
If (!($PortGroup)) {
|
||||
Write-Error "Virtual port group not found" -ErrorAction Stop
|
||||
Stop
|
||||
}
|
||||
If (@($PortGroup).count -gt 1) {
|
||||
Write-Error "Multiple port groups found" -ErrorAction Stop
|
||||
Stop
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Configure Guest OS Customization Spec"
|
||||
$NewOSSpecName = ("AutoBuild-$Hostname-" + (Get-Date -UFormat "%Y%m%d%H%M%S"))
|
||||
Write-Warning "NewOSSpecName = $NewOSSpecName"
|
||||
|
||||
Get-OSCustomizationSpec -Name 'Windows (Auto)' -Server $ViServer | New-OSCustomizationSpec -Name $NewOSSpecName -Type Persistent -Server $ViServer
|
||||
|
||||
Get-OSCustomizationSpec -Name $NewOSSpecName -Server $ViServer | `
|
||||
Set-OSCustomizationSpec `
|
||||
-NamingScheme fixed `
|
||||
-NamingPrefix $Hostname `
|
||||
-AdminPassword $GuestCredentialBB.GetNetworkCredential().Password
|
||||
|
||||
Get-OSCustomizationSpec -Name $NewOSSpecName -Server $ViServer | `
|
||||
Get-OSCustomizationNicMapping | `
|
||||
Set-OSCustomizationNicMapping `
|
||||
-IpMode UseStaticIP `
|
||||
-IpAddress $IpAddress.IPAddressToString `
|
||||
-SubnetMask $SubnetMask.IPAddressToString `
|
||||
-DefaultGateway $DefaultGateway.IPAddressToString `
|
||||
-Dns "10.2.7.40", "10.10.10.10"
|
||||
|
||||
$OSSpec = Get-OSCustomizationSpec -Name $NewOSSpecName -Server $ViServer
|
||||
|
||||
Set-Location C:\Temp
|
||||
$NewVMParams = @{
|
||||
Name = $FQDN;
|
||||
ResourcePool = $ComputeCluster.Name;
|
||||
Datastore = $DatastoreCluster;
|
||||
DiskStorageFormat = $DiskStorageFormat;
|
||||
Location = $FolderLocation;
|
||||
# Removed when using Content Library
|
||||
#Template = $Template;
|
||||
#OSCustomizationSpec = $OSSpec
|
||||
}
|
||||
|
||||
Get-ContentLibraryItem -Name $Template -Server $ViServer | New-VM @NewVMParams
|
||||
$VM = Get-VM -Name $FQDN
|
||||
$VM | Set-VM -OSCustomizationSpec $OSSpec -Confirm:$false
|
||||
|
||||
Write-Verbose -Message "Set vCenter Tags on VM"
|
||||
New-TagAssignment -Entity (Get-VM $FQDN -Server $VIServer) -Tag (Get-Tag -Server $ViServer -Category AppName -Name $AppName) -Server $VIServer
|
||||
New-TagAssignment -Entity (Get-VM $FQDN -Server $VIServer) -Tag (Get-Tag -Server $ViServer -Category DTAP -Name $VMEnvironment) -Server $VIServer
|
||||
New-TagAssignment -Entity (Get-VM $FQDN -Server $VIServer) -Tag (Get-Tag -Server $ViServer -Category LicensingRestrictions -Name $LicensingRestrictions) -Server $VIServer
|
||||
New-TagAssignment -Entity (Get-VM $FQDN -Server $VIServer) -Tag (Get-Tag -Server $ViServer -Category StartupPriority -Name $StartupPriority) -Server $VIServer
|
||||
|
||||
|
||||
# Ensure CPU/Memory Hot-Add Enabled
|
||||
$vmView = $VM | Get-View
|
||||
$vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
|
||||
$vmOptValCPU = New-Object VMware.Vim.OptionValue
|
||||
$vmOptValMem = New-Object VMware.Vim.OptionValue
|
||||
$vmOptValCPU.Key = "vcpu.hotadd"
|
||||
$vmOptValMem.Key = "mem.hotadd"
|
||||
$vmOptValCPU.Value = "true"
|
||||
$vmOptValMem.Value = "true"
|
||||
$vmConfigSpec.ExtraConfig += $vmOptValCPU
|
||||
$vmConfigSpec.ExtraConfig += $vmOptValMem
|
||||
$vmView.ReconfigVM($vmConfigSpec)
|
||||
|
||||
# Set CPU, Memory, Network
|
||||
$VM | Set-VM -NumCpu $CPU -MemoryGB $MemoryGB -Confirm:$false
|
||||
$VM | Get-NetworkAdapter | Set-NetworkAdapter -Portgroup $PortGroup -Confirm:$false
|
||||
|
||||
|
||||
|
||||
Write-Verbose -Message "Config and Update Disks"
|
||||
$VMDisk = $VM | Get-HardDisk
|
||||
$VMDisk1 = $VMDisk | Where-Object Name -EQ "Hard disk 1"
|
||||
$VMDisk2 = $VMDisk | Where-Object Name -EQ "Hard disk 2"
|
||||
$VMDisk3 = $VMDisk | Where-Object Name -EQ "Hard disk 3"
|
||||
|
||||
Write-Verbose -Message "Update Disk 1"
|
||||
$VMDisk1 = $VM | Get-HardDisk | Where-Object Name -EQ "Hard disk 1"
|
||||
If ($VMDisk1.CapacityGB -lt $DiskOsGB) {
|
||||
Set-HardDisk -HardDisk $VMDisk1 -CapacityGB $DiskOsGB -Confirm:$false
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Config Disk 2"
|
||||
If (!$VMDisk2) {
|
||||
$VM | New-HardDisk `
|
||||
-CapacityGB ($MemoryGB + 1) `
|
||||
-StorageFormat Thin `
|
||||
-DiskType Flat `
|
||||
-Persistence Persistent
|
||||
}
|
||||
$VMDisk2 = $VM | Get-HardDisk | Where-Object Name -EQ "Hard disk 2"
|
||||
If ($VMDisk2.CapacityGB -lt $DiskSwapGB) {
|
||||
Set-HardDisk -HardDisk $VMDisk2 -CapacityGB $DiskSwapGB -Confirm:$false
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Config Disk 3"
|
||||
If ($DiskDataGB -gt 0) {
|
||||
Write-Verbose -Message "$DiskDataGB greater than zero"
|
||||
If (!$VMDisk3) {
|
||||
$VM | New-HardDisk `
|
||||
-CapacityGB $DiskDataGB `
|
||||
-StorageFormat Thin `
|
||||
-DiskType Flat `
|
||||
-Persistence Persistent
|
||||
}
|
||||
$VMDisk3 = $VM | Get-HardDisk | Where-Object Name -EQ "Hard disk 3"
|
||||
If ($VMDisk3.CapacityGB -lt $DiskDataGB) {
|
||||
Set-HardDisk -HardDisk $VMDisk3 -CapacityGB $DiskDataGB -Confirm:$false
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Set VMware Tools Upgrade Policy to UpgradeAtPowerCycle"
|
||||
$VMView = $VM | Get-View
|
||||
$vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
|
||||
$vmConfigSpec.Tools = New-Object VMware.Vim.ToolsConfigInfo
|
||||
$vmConfigSpec.Tools.ToolsUpgradePolicy = "UpgradeAtPowerCycle"
|
||||
$VMView.ReconfigVM($vmConfigSpec)
|
||||
|
||||
Write-Verbose -Message "Power On VM"
|
||||
$VM | Start-VM
|
||||
|
||||
Write-Verbose -Message "[$FQDN]:Step 1 End"
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+667
@@ -0,0 +1,667 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
$NewITDWindowsVmVMwareParams = @{
|
||||
ComputerName = 'itdzmtest300.nd.gov';
|
||||
Environment = "Test";
|
||||
AppName = "ITD-POC-zmeier";
|
||||
Credential = $PrvCred;
|
||||
}
|
||||
|
||||
New-ITDWindowsVmVMware @NewITDWindowsVmVMwareParams
|
||||
#>
|
||||
|
||||
function New-ITDWindowsVmVMwareStep2 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$FQDN,
|
||||
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[string]
|
||||
$VMEnvironment,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
Write-Verbose -Message "Start New-ITDWindowsVmVMwareStep2"
|
||||
$FQDN = $FQDN.ToLower()
|
||||
$HostName = $FQDN.split('.')[0]
|
||||
$DomainName = $FQDN.Substring($FQDN.IndexOf(".") + 1)
|
||||
|
||||
$VM = Get-VM -Name $FQDN
|
||||
$ViServer = $VM.Uid.split('@')[1].split(':')[0]
|
||||
$VMTagAssignments = Get-TagAssignment -Entity $VM
|
||||
|
||||
#$AppName = $VMTagAssignments | Where-Object { $_.Tag.Category.Name -eq "AppName" }
|
||||
#$DTAP = $VMTagAssignments | Where-Object { $_.Tag.Category.Name -eq "DTAP" }
|
||||
|
||||
Write-Verbose -Message "FQDN: $FQDN"
|
||||
Write-Verbose -Message "HostName: $HostName"
|
||||
|
||||
$GuestVMLocalCredential = Get-ITDPassword -Title $FQDN -UserName itdadmin -Credential $Credential -ErrorAction SilentlyContinue
|
||||
$GuestCredentialAB = New-Object System.Management.Automation.PSCredential ('itdadmin', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
$GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($GuestVMLocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
|
||||
|
||||
If ($null -eq $GuestVMLocalCredential) {
|
||||
Write-Error -Message "Credentials not found in Passwordstate" -ErrorAction Stop
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Credentials found in Passwordstate"
|
||||
Write-Verbose -Message ("username: + " + $GuestVMLocalCredential.UserName)
|
||||
}
|
||||
|
||||
<# Wait for Customization to finish
|
||||
$VMStarted = $false
|
||||
$VMCustomizationStarted = $false
|
||||
$VMCustomizationResult = $false
|
||||
|
||||
While ($VMStarted -eq $false -or $VMCustomizationStarted -eq $false -or $VMCustomizationResult -eq $false) {
|
||||
Write-Warning ("Customization wait loop started " + (Get-Date))
|
||||
Write-Verbose "Current Status:"
|
||||
Write-Verbose ("VMStarted: " + $VMStarted)
|
||||
Write-Verbose ("VMCustomizationStarted: " + $VMCustomizationStarted)
|
||||
Write-Verbose ("VMCustomizationResult: " + $VMCustomizationResult)
|
||||
$GetVIEventRuntime = Measure-Command -Expression { $VMEvents = Get-VIEvent -Entity $VM -Server $ViServer -ErrorAction SilentlyContinue } ## takes a long time to execute
|
||||
Write-Verbose ("Get-VIEvent last run time: " + $GetVIEventRuntime.TotalSeconds + " seconds")
|
||||
If ($VMStarted -eq $false) {
|
||||
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "VMStartingEvent" })) {
|
||||
$VMStarted = $true
|
||||
Write-Warning "[$FQDN]:Virtual machine started"
|
||||
}
|
||||
}
|
||||
If ($VMCustomizationStarted -eq $false) {
|
||||
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "CustomizationStartedEvent" })) {
|
||||
$VMCustomizationStarted = $true
|
||||
Write-Warning "[$FQDN]:Virtual machine customization started"
|
||||
}
|
||||
}
|
||||
If ($VMCustomizationResult -eq $false) {
|
||||
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "CustomizationFailed" })) {
|
||||
$VMCustomizationResult = $true
|
||||
Write-Error "[$FQDN]:Virtual machine customization failed"
|
||||
Exit
|
||||
Exit
|
||||
}
|
||||
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "CustomizationSucceeded" })) {
|
||||
$VMCustomizationResult = $true
|
||||
Write-Warning "[$FQDN]:Virtual machine customization completed"
|
||||
}
|
||||
}
|
||||
} #>
|
||||
|
||||
|
||||
# OS
|
||||
Write-Verbose -Message "Begin Post-Sysprep OS Guest Customization"
|
||||
Write-Verbose -Message "Assigning WMI Tag 000-Prod, SCCM will change it later if required"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Move DVD Drive Mount
|
||||
try {
|
||||
Write-Verbose "Create new Class"
|
||||
$Class = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
|
||||
|
||||
$Class["__CLASS"] = "ITD";
|
||||
$Class.Qualifiers.Add("Static", $true)
|
||||
$Class.Properties.Add("MyKey", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties["MyKey"].Qualifiers.Add("Key", $true)
|
||||
|
||||
$Class.Properties.Add("LastModified", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties.Add("DTAP", [System.Management.CimType]::String, $false)
|
||||
$Class.Properties.Add("Baseline", [System.Management.CimType]::String, $false)
|
||||
|
||||
$Class.Put()
|
||||
|
||||
Write-Verbose "Create single ITD Object"
|
||||
Set-WmiInstance -Class ITD -Arguments @{LastModified = (Get-Date); DTAP = "Prod"; Baseline = "000" }
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Checking for DVD drive, and moving it to Z:\"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
Write-Host "hello1"
|
||||
# Move DVD Drive Mount
|
||||
try {
|
||||
$dvd_letter = 'Z'
|
||||
$dvd = Get-WmiObject -Class Win32_Volume -Filter "DriveType=5" | Select-Object -First 1
|
||||
if ($dvd.Name -notmatch $dvd_letter) {
|
||||
Write-Verbose -Message "Found DVD drive, switching to $dvd_letter`:"
|
||||
|
||||
Set-WmiInstance -InputObject $dvd -Arguments @{DriveLetter = "$dvd_letter`:" } | Out-Null
|
||||
|
||||
Write-Verbose -Message "DVD drive moved to drive letter $dvd_letter`:"
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "No DVD drive changes required, continuing..."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Checking for unpartitioned space on C: volume and expanding"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
Write-Host "hello1"
|
||||
# Expand C: Partition To Maximum Extent
|
||||
try {
|
||||
$cSize = ( Get-Partition -DriveLetter C ).Size
|
||||
$cMaxSize = ( Get-PartitionSupportedSize -DriveLetter C ).SizeMax
|
||||
|
||||
if ($cSize -lt $cMaxSize) {
|
||||
Write-Verbose -Message "Expanding C: from $($csize / 1GB)GB to $($cMaxSize / 1GB)GB..."
|
||||
|
||||
Resize-Partition -DriveLetter C -Size $cMaxSize
|
||||
|
||||
Write-Verbose -Message "C: expanded to $($cMaxSize / 1GB)GB."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "C: is already at maximum size, continuing..."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Start Extra Disk(s) config"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Initialize Additional Disks
|
||||
try {
|
||||
# Non-initialized and MBR-initialized disks will have 0 partitions by default, but GPT-initialized disks will have 1 system reserved partition by default
|
||||
$Disks = Get-Disk | Where-Object { $_.NumberOfPartitions -eq 0 -or ( $_.PartitionStyle -eq 'GPT' -and $_.NumberOfPartitions -eq 1 ) } | Sort-Object -Property Number
|
||||
|
||||
If ($Disks) {
|
||||
Write-Verbose -Message "Found $(@($disks).Count) unpartitioned disks."
|
||||
|
||||
ForEach ($disk in $disks) {
|
||||
if ($disk.IsOffline) {
|
||||
Set-Disk $disk.Number -IsOffline $false
|
||||
Write-Verbose -Message "Brought disk $($disk.Number)($("{0:n0}GB" -f ($disk.Size / 1GB))) online..."
|
||||
}
|
||||
|
||||
if ($disk.IsReadOnly) { Set-Disk $disk.Number -IsReadOnly $false }
|
||||
if ($disk.PartitionStyle -eq 'RAW') { Initialize-Disk $disk.Number -PartitionStyle GPT -ErrorAction SilentlyContinue }
|
||||
|
||||
$diskParam = @{
|
||||
FileSystem = 'NTFS'
|
||||
Confirm = $false
|
||||
}
|
||||
|
||||
$driveLetter = [Int][Char]'D'
|
||||
while (Get-Volume -DriveLetter $([Char]$driveLetter) -ErrorAction SilentlyContinue) {
|
||||
$driveLetter++
|
||||
}
|
||||
|
||||
$diskParam.DriveLetter = [Char]$driveLetter
|
||||
|
||||
if (@($disks).IndexOf($disk) -eq 0 -and (-not (Get-Volume -DriveLetter D -ErrorAction SilentlyContinue))) {
|
||||
$diskParam.NewFileSystemLabel = 'Temporary Storage'
|
||||
}
|
||||
elseif (@($disks).IndexOf($disk) -eq 1 -and (-not (Get-Volume -DriveLetter E -ErrorAction SilentlyContinue))) {
|
||||
$diskParam.NewFileSystemLabel = 'Data'
|
||||
}
|
||||
|
||||
[void](New-Partition -DiskNumber $disk.Number -DriveLetter $diskParam.DriveLetter -UseMaximumSize)
|
||||
[void](Format-Volume @diskParam)
|
||||
}
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "No unpartitioned disks found, continuing..." -Verbose
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Start Page File Configuration"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Configure Page File
|
||||
if (Get-Partition -DriveLetter D -ErrorAction SilentlyContinue) {
|
||||
Write-Verbose -Message "Setting up pagefile.sys on D:..." -Verbose
|
||||
|
||||
try {
|
||||
if (-not [IO.File]::Exists('D:\pagefile.sys')) {
|
||||
$autoPage = Get-WmiObject -Class Win32_ComputerSystem -EnableAllPrivileges
|
||||
$autoPage.AutomaticManagedPagefile = $false
|
||||
[void]$autoPage.Put()
|
||||
|
||||
Write-Verbose -Message "Disabled automatic pagefile management." -Verbose
|
||||
|
||||
$pageFile = Get-WmiObject -Class Win32_PageFileSetting -EnableAllPrivileges
|
||||
$pageFile.Delete()
|
||||
|
||||
Write-Verbose -Message "Deleted C:\pagefile.sys." -Verbose
|
||||
|
||||
Set-WmiInstance -Class Win32_PageFileSetting -Arguments @{ Name = "D:\pagefile.sys"; InitialSize = 0; MaximumSize = 0; } -EnableAllPrivileges | Out-Null
|
||||
|
||||
Write-Verbose -Message "System managed page file created on D:\pagefile.sys." -Verbose
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Pagefile already configured on D:, continuing..." -Verbose
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Page file drive not found, cannot set up page file. Continuing server configuration..." -Verbose
|
||||
Write-Warning "Page file drive not found, cannot set up page file. Continuing server configuration..."
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Enabling Remote Management"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Configure Remote Management (RDP/PoSH)
|
||||
try {
|
||||
Write-Verbose -Message "Checking WinRM..." -Verbose
|
||||
|
||||
if (Test-WSMan -ErrorAction SilentlyContinue) {
|
||||
Write-Verbose -Message "WinRM is already enabled."
|
||||
}
|
||||
else {
|
||||
Enable-PSRemoting -Force
|
||||
|
||||
Write-Verbose -Message "WinRM is now enabled."
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Checking RDP..." -Verbose
|
||||
|
||||
$RDP = Get-WmiObject Win32_TerminalServiceSetting -Namespace root\cimv2\TerminalServices
|
||||
$NLA = Get-WmiObject Win32_TSGeneralSetting -Namespace root\cimv2\TerminalServices -Filter "TerminalName='RDP-tcp'"
|
||||
if ($RDP.AllowTSConnections -eq 0) {
|
||||
Write-Verbose -Message "RDP is disabled, enabling..."
|
||||
|
||||
$RDP.SetAllowTSConnections(1, 1) | Out-Null
|
||||
|
||||
Write-Verbose -Message "RDP is enabled."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "RDP is already enabled, checking NLA security..."
|
||||
}
|
||||
|
||||
if ($NLA.UserAuthenticationRequired -eq 0) {
|
||||
Write-Verbose -Message "RDP is not NLA secured, enabling..."
|
||||
|
||||
$NLA.SetUserAuthenticationRequired(1) | Out-Null
|
||||
|
||||
Write-Verbose -Message "RDP is now NLA secured."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "RDP is already NLA secured."
|
||||
}
|
||||
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Checking current power plan, set to High Performance"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Configure Power Plan
|
||||
try {
|
||||
$powerPlans = powercfg -l
|
||||
|
||||
if ($powerPlans -match '\*$' -notmatch 'High performance') {
|
||||
$currentPlan = [regex]::Match($powerPlans, '(?<=(\())[^)]+(?=(\)\s\*))').Value
|
||||
|
||||
Write-Verbose -Message "Power plan is currently set to $currentPlan, changing to High Performance..." -Verbose
|
||||
|
||||
$highPerformance = [regex]::Match($powerPlans, '([\d\w-\S]+)(?=\s+\(High performance\))').Value
|
||||
[void](powercfg -setactive $highPerformance)
|
||||
|
||||
Write-Verbose -Message "Power plan set to High Performance." -Verbose
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Power plan already configured for High Performance." -Verbose
|
||||
}
|
||||
|
||||
[void](& w32tm.exe /resync /nowait)
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$TimeSyncFunc = {
|
||||
# Configure Time/Date Settings
|
||||
Write-Verbose -Message "Checking current time/date settings..."
|
||||
$Domain = "<DomainName>"
|
||||
try {
|
||||
if ((Get-TimeZone).Id -ne 'Central Standard Time') {
|
||||
Write-Verbose -Message "Current time zone set to $((Get-TimeZone).Id), setting to Central Standard Time." -Verbose
|
||||
|
||||
Set-TimeZone -Id 'Central Standard Time'
|
||||
|
||||
Write-Verbose -Message "Time zone set to Central Standard Time." -Verbose
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Time zone is already set to Central Standard Time." -Verbose
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
$TimeSyncScriptBlock = $TimeSyncFunc -replace '<DomainName>', $DomainName
|
||||
$VM | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText $TimeSyncScriptBlock
|
||||
#>
|
||||
Write-Verbose -Message "Enable the server manager performance monitors"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Enable Performance Counters
|
||||
|
||||
|
||||
try {
|
||||
if (Get-ScheduledTask -TaskName "Server Manager Performance Monitor" | Where-Object State -NE "Running" -ErrorAction SilentlyContinue) {
|
||||
Enable-ScheduledTask -TaskPath "\Microsoft\Windows\PLA\" -TaskName "Server Manager Performance Monitor" | Start-ScheduledTask
|
||||
|
||||
Write-Verbose -Message "Performance monitors enabled." -Verbose
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Performance monitors already enabled, continuing..." -Verbose
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Disable Windows Firewall"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText {
|
||||
# Disable Windows Firewall
|
||||
Write-Verbose -Message "Checking for active Windows Firewall..." -Verbose
|
||||
|
||||
if ((Get-NetFirewallProfile).Enabled -contains 'True') {
|
||||
Write-Verbose -Message "Windows Firewall is still enabled, disabling it..."
|
||||
|
||||
Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
|
||||
|
||||
Write-Verbose -Message "Windows Firewall disabled." -Verbose
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Windows Firewall already disabled, continuing..." -Verbose
|
||||
}
|
||||
}
|
||||
|
||||
# Active Directory
|
||||
Write-Verbose -Message "Join Active Directory"
|
||||
Write-Verbose -Message "Domain is $DomainName"
|
||||
Write-Verbose -Message "AppName is $AppName"
|
||||
|
||||
switch ($DomainName) {
|
||||
'nd.gov' {
|
||||
$SearchBaseDomain = "dc=nd,dc=gov"
|
||||
}
|
||||
'ndcloud.gov' {
|
||||
$SearchBaseDomain = "dc=ndcloud,dc=gov"
|
||||
}
|
||||
}
|
||||
### Shared-PeopleSoft-HigherEd is not found because of the extra space character in the OU name
|
||||
### commented out section below is the original code, swapped out for new code 2025/01/13
|
||||
|
||||
|
||||
|
||||
# Determine if the AppName has a dedicated OU, and pick it... or choose the All-General OU
|
||||
If ($DomainName -eq 'nd.gov') {
|
||||
$OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq $AppName }
|
||||
If ($null -eq $OUAppName) {
|
||||
Write-Verbose -Message "Dedicated AppName OU not found, placing in All-General OU"
|
||||
$OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq "All-General" }
|
||||
}
|
||||
|
||||
# Determine if the Environment sub-ou is special for the selected AppName
|
||||
switch ($OUAppName.Name) {
|
||||
'Shared-PeopleSoft-HigherEd' {
|
||||
switch ($VMEnvironment) {
|
||||
'Test' { $EnvString = "Non-Prod" }
|
||||
'Production' { $EnvString = "Prod" }
|
||||
}
|
||||
}
|
||||
'Shared-PeopleSoft-State' {
|
||||
switch ($VMEnvironment) {
|
||||
'Test' { $EnvString = "Non-Prod" }
|
||||
'Production' { $EnvString = "Prod" }
|
||||
}
|
||||
}
|
||||
Default {
|
||||
switch ($VMEnvironment) {
|
||||
'Test' { $EnvString = "Test" }
|
||||
'Production' { $EnvString = "Prod" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Gather all sub OUs for the AppName OU
|
||||
$SubOUs = Get-ADOrganizationalUnit -Server $DomainName -SearchBase $OUAppName -Filter *
|
||||
If (@($SubOUs).count -eq 1) {
|
||||
$OUToUse = $SubOUs
|
||||
}
|
||||
Else {
|
||||
$OUToUse = $SubOUs | Where-Object { $_.DistinguishedName -like ("OU=$EnvString,OU=" + $OUAppName.Name + "*") }
|
||||
}
|
||||
|
||||
# Determine if AD Computer object already exists
|
||||
$ExistingADComputer = Get-ADComputer -Filter { Name -eq $HostName }
|
||||
If ($ExistingADComputer) {
|
||||
Write-Verbose -Message "AD Object already exists in AD"
|
||||
# Determine if AD Computer object is in the correct OU, or a sub-OU of the correct OU
|
||||
If ($ExistingADComputer.DistinguishedName -like ("*" + $OUToUse.DistinguishedName)) {
|
||||
Write-Verbose -Message "AD Object is in the correct OU, will attempt domain join"
|
||||
$AttemptDomainJoin = $true
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "AD Object is NOT in the correct OU, domain join will not occur, needs human review"
|
||||
$AttemptDomainJoin = $false
|
||||
}
|
||||
}
|
||||
Else {
|
||||
$AttemptDomainJoin = $true
|
||||
}
|
||||
|
||||
If ($AttemptDomainJoin) {
|
||||
$OuFinal = $OUToUse.DistinguishedName
|
||||
|
||||
$FirstScriptBlock = { $DomainJoinCred = New-Object System.Management.Automation.PSCredential('svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force)) }
|
||||
$SecondScriptText = 'Add-Computer -DomainName <DomainName> -OUPath "<OuPath>" -Credential $DomainJoinCred -Server itddc42.nd.gov'
|
||||
$SecondScriptText = $SecondScriptText -replace '<DomainName>', $DomainName
|
||||
$SecondScriptText = $SecondScriptText -replace '<OuPath>', $OuFinal
|
||||
|
||||
Write-Verbose -Message "[$FQDN]:Invoke-VMScript to AD join"
|
||||
$InvokeVMScriptFunc = [System.Management.Automation.ScriptBlock]::Create("$FirstScriptBlock ; $SecondScriptText")
|
||||
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText $InvokeVMScriptFunc
|
||||
|
||||
Write-Warning -Message "[$FQDN]:Restart VMGuest, wait for Tools, then 90 seconds after"
|
||||
Get-VM -Name $FQDN | Restart-VMGuest -Confirm:$false
|
||||
Wait-Tools -VM (Get-VM -Name $FQDN)
|
||||
Start-Sleep -Seconds 90
|
||||
}
|
||||
}
|
||||
else {
|
||||
$GuestCredentialAB = $GuestCredentialBB
|
||||
}
|
||||
<#
|
||||
Write-Verbose -Message ("[$FQDN]:Copying SCCM client installer to C:\temp... " + (Get-Date))
|
||||
|
||||
Copy-VMGuestFile -Source C:\SCCM_Client\ -Destination C:\temp\SCCM_Client -VM (Get-VM -Name $FQDN) -LocalToGuest -GuestCredential $GuestCredentialAB -Force
|
||||
Write-Verbose -Message ("[$FQDN]:SCCM client copy complete " + (Get-Date))
|
||||
#E:\AutoBuild\SCCM_Client\
|
||||
|
||||
# Check if SCCM automatically installed the SCCM client and registered it
|
||||
$CcmRegistered = $false
|
||||
$CcmRegistration = Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||||
Get-Content C:\windows\ccm\logs\ClientIDManagerStartup.log
|
||||
}
|
||||
If ($CcmRegistration.ScriptOutput -match "Client is registered") {
|
||||
Write-Warning "Client is registered."
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
ElseIf ($CcmRegistration.ScriptOutput -match "Client is already registered") {
|
||||
Write-Warning "Client is already registered."
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
If ($CcmRegistered -eq $false) {
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||||
|
||||
If (Get-Process -Name ccmsetup -ErrorAction SilentlyContinue) {
|
||||
Write-Warning "CCM client is already installing"
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
ElseIf (Get-Process -Name ccmexec -ErrorAction SilentlyContinue) {
|
||||
Write-Warning "CCM client is already installed"
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
Else {
|
||||
Write-Warning -Message "Installing SCCM Client..."
|
||||
Invoke-Expression -Command "C:\temp\SCCM_Client\ccmsetup.exe SMSSITECODE=ITD SMSMP=itdsccmp2.nd.gov DNSSUFFIX=nd.gov"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose -Message "[$FQDN]:Register SCCM Client"
|
||||
While ($CcmRegistered -eq $false) {
|
||||
$CcmRegistration = Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||||
Get-Content C:\windows\ccm\logs\ClientIDManagerStartup.log
|
||||
}
|
||||
If ($CcmRegistration.ScriptOutput -match "Client is registered") {
|
||||
Write-Warning "Client is registered."
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
ElseIf ($CcmRegistration.ScriptOutput -match "Client is already registered") {
|
||||
Write-Warning "Client is already registered."
|
||||
$CcmRegistered = $true
|
||||
}
|
||||
ElseIf ($CcmRegistered -eq $false) { Start-Sleep -Seconds 30 }
|
||||
}
|
||||
|
||||
Write-Verbose -Message "[$FQDN]:Approve SCCM Client"
|
||||
#Start-Sleep -Seconds 30 # ADD LOOP/SMARTS TO WAIT FOR DISCOVERY AND ANOTHER FOR APPROVAL
|
||||
Invoke-Command -ComputerName itdsccmp2.nd.gov -Credential $Credential -ArgumentList $Hostname -ScriptBlock {
|
||||
Import-Module 'D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
|
||||
$PSDrives = Get-PSDrive
|
||||
If ($PSDrives | Where-Object Name -EQ "ITD") {
|
||||
# ITD Drive exists, do nothing
|
||||
}
|
||||
else {
|
||||
New-PSDrive -Name "ITD" -PSProvider AdminUI.PS.Provider\CMSite -Root itdsccmp2.nd.gov
|
||||
}
|
||||
|
||||
Set-Location ITD:\
|
||||
$Device = Get-CMDevice -Name $args[0]
|
||||
If ($Device.IsApproved -eq 0) {
|
||||
Approve-CMDevice -DeviceName $args[0]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Write-Verbose -Message "Trigger SCCM MachinePolicy First Check-in"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000021}");
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Trigger SCCM MachinePolicy"
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000021}");
|
||||
}
|
||||
|
||||
<#Write-Verbose -Message "Waiting for network connectivity / Then KVM Activation..."
|
||||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||||
# Pause until network connectivity is available
|
||||
$KMS = 'kms.nd.gov'
|
||||
|
||||
try {
|
||||
$nwJob = Start-Job -Name 'NetworkCheck' -ScriptBlock {
|
||||
Param ( [String]$KMS )
|
||||
do {
|
||||
$nwStatus = Test-NetConnection -ComputerName $KMS -Port 1688 -InformationLevel Quiet
|
||||
|
||||
Start-Sleep -Seconds 10
|
||||
} until($nwStatus)
|
||||
} -ArgumentList $KMS
|
||||
|
||||
# If after 30 seconds the network connection is not responding continue on
|
||||
if ((Wait-Job -Job $nwJob -Timeout 30).State -eq 'Completed') {
|
||||
Write-Verbose -Message 'Network connectivity has been verified.'
|
||||
}
|
||||
else {
|
||||
[void](Stop-Job -Job $nwJob)
|
||||
Write-Verbose -Message 'Network connectivity could not be verified.'
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
|
||||
Activate via KMS
|
||||
Write-Verbose -Message "Activating windows against $KMS..."
|
||||
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit
|
||||
}
|
||||
try {
|
||||
cscript C:\Windows\System32\slmgr.vbs /skms $KMS | Out-Null
|
||||
cscript C:\Windows\System32\slmgr.vbs /ato | Out-Null
|
||||
|
||||
Write-Verbose -Message "Checking activation status..."
|
||||
|
||||
$kmsOut = cscript C:\Windows\System32\slmgr.vbs /dli
|
||||
|
||||
if (($kmsOut | Select-String -Pattern '^License Status:') -match 'Licensed') {
|
||||
Write-Verbose -Message "Windows successfully activated."
|
||||
}
|
||||
else {
|
||||
Write-Verbose -Message "Windows failed to activate, run slmgr commands manually. Ensure server time is correct."
|
||||
Write-Warning -Message "Windows failed to activate, run slmgr commands manually. Ensure server time is correct."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Throw $_
|
||||
Break
|
||||
}
|
||||
}
|
||||
#>
|
||||
Write-Verbose -Message ("[$FQDN]:Add to Solarwinds")
|
||||
$Func = {
|
||||
param($C)
|
||||
Import-Module -Name ITDSolarwinds -Verbose
|
||||
Import-SWDiscovery -ComputerName $C -Integration ServiceNow
|
||||
}
|
||||
Invoke-Command -ComputerName itdslrwnds.nd.gov -ScriptBlock $Func -ArgumentList $FQDN -Credential $Credential
|
||||
Write-Verbose -Message "[$FQDN]:End"
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Decomissions a Windows Server device
|
||||
.DESCRIPTION
|
||||
Decomissions a Windows Server device with ITD specifications (DNS/Infoblox, Active Directory, SCCM)
|
||||
.NOTES
|
||||
Credential must be an nd.gov account with access to remove objects on all platforms involved: DNS/Infoblox, Active Directory, SCCM. Read access to vCenter in the future.
|
||||
.EXAMPLE
|
||||
Remove-ITDWindowsServer -ComputerName itdxxx.nd.gov -SCTaskNum SCTASKxxxxxxxxx -Credential $AdminCredential
|
||||
#>
|
||||
|
||||
function Remove-ITDWindowsServer {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[string]
|
||||
$SCTaskNum,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
$RadiusCred = New-Object System.Management.Automation.PSCredential($Credential.username.split('\')[1], ($Credential.Password))
|
||||
}
|
||||
|
||||
process {
|
||||
$HostName = $ComputerName.split('.')[0]
|
||||
|
||||
# get current user, SCTask, Ritm, custom variables
|
||||
switch ($env:username) {
|
||||
'svcitdiaasauto' {
|
||||
$assignTo = Get-ITDServiceNowUser -Username svcvmwareadm
|
||||
}
|
||||
Default {
|
||||
$assignTo = Get-ITDServiceNowUser -Username $Env:username
|
||||
}
|
||||
}
|
||||
|
||||
$SCTask = Get-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum
|
||||
$RitmNum = $SCTask.request_item.display_value
|
||||
|
||||
Write-Verbose -Message "Gathering $RitmNum"
|
||||
$Ritm = Get-ITDServiceNowRecord -ItemType 'Request Item' -Number $RitmNum -IncludeVariableSet -ErrorAction Stop
|
||||
|
||||
###### the name in the $ComputerName parameter must match a name in the form to continue
|
||||
Write-Verbose -Message "Gathering VariableSet data from $RitmNum"
|
||||
$MatchFound = $false
|
||||
ForEach ($Row in $Ritm.VariableSet) {
|
||||
$TempCi = Get-ITDServiceNowRecord -Table cmdb_ci -SysId ($Row.host_name_ref) -ErrorAction Stop
|
||||
If ($ComputerName -eq $TempCi.FQDN.display_value) {
|
||||
$Ci = $TempCi
|
||||
$MatchFound = $true
|
||||
}
|
||||
}
|
||||
|
||||
$FQDN = $Ci.fqdn.display_value
|
||||
|
||||
If ($MatchFound -eq $false) {
|
||||
Write-Error -Message "ComputerName $ComputerName was not found in VariableSet for $RitmNum" -ErrorAction Stop
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Assigning SCTask to current user, maybe"
|
||||
##### Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{assigned_to = $assignTo.name } REVIEW IF ASSIGNMENT SHOULD BE MADE HERE
|
||||
$short_description = $SCTask.short_description.display_value
|
||||
|
||||
# Gather DNS FQDN and IP
|
||||
$DNSResolve = Resolve-DnsName -Name $ComputerName -ErrorAction SilentlyContinue
|
||||
If ($DNSResolve -eq $null) {
|
||||
$DNSResolve = "DNS object not found"
|
||||
}
|
||||
$DNSInfo = $DNSResolve | ConvertTo-Json -WarningAction SilentlyContinue
|
||||
|
||||
# AD OU
|
||||
$ADComputers = $null
|
||||
$Domain = $ComputerName.Substring($ComputerName.IndexOf('.') + 1)
|
||||
switch ($Domain) {
|
||||
'nd.gov' {
|
||||
try {
|
||||
$ADComputers = Get-ADComputer -Identity $HostName -Properties * -ErrorAction SilentlyContinue
|
||||
}
|
||||
catch {
|
||||
# empty because erroraction silentlycontinue doesn't work as expected
|
||||
}
|
||||
If ($ADComputers) {
|
||||
$ADInfo = $ADComputers | ConvertTo-Json -WarningAction SilentlyContinue
|
||||
}
|
||||
Else {
|
||||
$ADInfo = "AD object not found"
|
||||
}
|
||||
}
|
||||
'Default' {
|
||||
Write-Warning -Message "$ComputerName not nd.gov, review other domains manually"
|
||||
}
|
||||
}
|
||||
|
||||
# SCCM collections
|
||||
$SCCMResult = Invoke-Command -ComputerName itdsccmp2.nd.gov -Credential $Credential -ArgumentList $Hostname -ScriptBlock {
|
||||
Import-Module 'D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
|
||||
Set-Location ITD:\
|
||||
$CMDevice = Get-CMDevice -Name $args[0]
|
||||
If ($CMDevice) {
|
||||
Get-ITDCMDeviceMemberOf -ComputerName $args[0]
|
||||
}
|
||||
Else {
|
||||
"SCCM object not found"
|
||||
}
|
||||
}
|
||||
$SCCMInfo = $SCCMResult | ConvertTo-Json -WarningAction SilentlyContinue
|
||||
|
||||
Write-Verbose -Message "Updating SCTask with discovered information"
|
||||
Update-ITDServiceNowRecord -ItemType "Catalog Task" -Number $SCTaskNum -Values @{work_notes = ("DNS Information: `n " + $DNSInfo) }
|
||||
|
||||
If ($vCenterInfo) {
|
||||
|
||||
}
|
||||
If ($AzureInfo) {
|
||||
|
||||
}
|
||||
|
||||
Update-ITDServiceNowRecord -ItemType "Catalog Task" -Number $SCTaskNum -Values @{work_notes = ("Active Directory Information: `n " + $ADInfo) }
|
||||
Update-ITDServiceNowRecord -ItemType "Catalog Task" -Number $SCTaskNum -Values @{work_notes = ("SCCM Information: `n " + $SCCMInfo) }
|
||||
|
||||
$CommentsForWorkNotes = $null
|
||||
|
||||
try {
|
||||
$Notes = '<div><strong><span style="color: #c00000;">Decommissioned ' + (Get-Date -UFormat "%Y/%m/%d %H:%M:%S") + '</span></strong></div>'
|
||||
Write-Verbose -Message "All records with Title matching $ComputerName were marked as decommissioned in the Notes field."
|
||||
Update-ITDPassword -Title $ComputerName -AppendNotes -Notes $Notes -Credential $Credential -Force
|
||||
$CommentsForWorkNotes += ("`nPasswordstate: All records with Title matching $ComputerName were marked as decommissioned in the Notes field. ")
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
|
||||
# Power off VM
|
||||
switch ($Ci.model_id.display_value) {
|
||||
{ $_ -like "*VMware*" } {
|
||||
$hardware_platform = "VMware";
|
||||
$hardware_type = 'Virtual Machine'
|
||||
|
||||
$VMs = Get-VM -Name $FQDN -ErrorAction SilentlyContinue | Where-Object { $_.ExtensionData.summary.config.ManagedBy.Type -ne "placeholderVm" }
|
||||
switch ( @($VMs).count ) {
|
||||
{ 0 } {
|
||||
Write-Warning "$FQDN not found in vCenter... is it Azure? Or does it not exist?"
|
||||
}
|
||||
{ $_ -gt 1 } {
|
||||
Write-Verbose -Message "Multiple virtual machines with name $FQDN were found."
|
||||
Write-Error -Message ("Multiple virtual machines with name $FQDN were found. Are there SRM placeholders? If so, unconfigure SRM and run this again. If there are no placeholders, confirm the virtual machine name.") -ErrorAction Stop
|
||||
}
|
||||
1 {
|
||||
Write-Verbose -Message 'One virtual machine with name $FQDN were found.'
|
||||
$TagAssignment = Get-TagAssignment -Entity $VMs
|
||||
$vCenterInfo = $TagAssignment | select Tag, Entity | ConvertTo-Json -Depth 1
|
||||
If ($VMs.PowerState -eq 'PoweredOn') {
|
||||
Write-Verbose -Message "Power off VMware VM $FQDN"
|
||||
$CommentsForWorkNotes += ("`nVMware: VM $FQDN has been powered off. ")
|
||||
$VMs | Stop-VM -Confirm:$false
|
||||
}
|
||||
|
||||
If ($vCenterInfo) {
|
||||
# enter work_notes into sctask
|
||||
Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{
|
||||
work_notes = ("vCenter Information: `n " + $vCenterInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{ $_ -like "*Microsoft Virtual Machine*" } {
|
||||
$hardware_platform = "Azure";
|
||||
$hardware_type = 'Virtual Machine'
|
||||
}
|
||||
{ $_ -like "*HP*" } {
|
||||
$hardware_platform = 'HPE';
|
||||
$hardware_type = 'Physical'
|
||||
}
|
||||
default { $hardware_platform = 'Other' }
|
||||
}
|
||||
|
||||
# SCCM removal
|
||||
If ($SCCMResult -eq "SCCM object not found") {
|
||||
# do nothing
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Attempt SCCM removal"
|
||||
try {
|
||||
$Domain = $ComputerName.Substring($ComputerName.IndexOf('.') + 1)
|
||||
switch ($Domain) {
|
||||
'nd.gov' { $SCCMDomain = 'NDGOV' }
|
||||
'itd.nd.gov' { $SCCMDomain = 'NDGOV' }
|
||||
'k12.nd.us' { $SCCMDomain = 'K12' }
|
||||
'stg.k12.nd.us' { $SCCMDomain = 'K12STG' }
|
||||
'ndcloud.gov' { $SCCMDomain = 'NDCLOUD'}
|
||||
}
|
||||
|
||||
Write-Verbose -Message ("$Computername is SCCM Domain $SCCMDomain")
|
||||
|
||||
Invoke-Command -ComputerName itdsccmp2.nd.gov -Credential $Credential -ArgumentList $Hostname, $SCCMDomain -ScriptBlock {
|
||||
Import-Module 'D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
|
||||
Set-Location ITD:\
|
||||
$Devices = Get-CMDevice -Name $args[0] | Where-Object Domain -EQ $args[1]
|
||||
$Devices | select Name, Domain
|
||||
ForEach ($Device in $Devices) {
|
||||
If ($Device.Domain -match $SCCMDomain) {
|
||||
Write-Verbose -Message ("SCCM: Removing " + $Device.Name + " on " + $Device.Domain + " domain")
|
||||
$Device | Remove-CMDevice -Confirm:$false -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$CommentsForWorkNotes += ("`nSCCM: Device named $Hostname was removed. ")
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
# Active Directory removal
|
||||
If ($ADInfo -eq "AD object not found") {
|
||||
# do nothing
|
||||
}
|
||||
Else {
|
||||
try {
|
||||
switch ($Domain) {
|
||||
'nd.gov' {
|
||||
$ADComputers = Get-ADComputer -Identity $HostName
|
||||
switch ( @($ADComputers).count ) {
|
||||
{ 0 } { "AD: Not found" }
|
||||
{ $_ -gt 1 } { "AD: More than one found" }
|
||||
{ 1 } {
|
||||
Write-Verbose -Message "AD: One found, removing"
|
||||
$ADComputers
|
||||
$ADComputers | Remove-ADObject -Recursive -Credential $Credential -Confirm:$false
|
||||
}
|
||||
}
|
||||
$CommentsForWorkNotes += ("`nActive Directory: AD computer object with name $Hostname was removed from the nd.gov domain. ")
|
||||
|
||||
}
|
||||
'Default' {
|
||||
Write-Warning -Message "$ComputerName not nd.gov"
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
# DNS removal
|
||||
If ($DNSResolve -eq "DNS object not found") {
|
||||
# do nothing
|
||||
}
|
||||
Else {
|
||||
try {
|
||||
Write-Verbose "Attempting DNS removal"
|
||||
Remove-ITDIbDnsRecord -ComputerName $ComputerName -Credential $RadiusCred
|
||||
$CommentsForWorkNotes += ("`nInfoblox: DNS A Record was removed for $ComputerName. ")
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
# Solarwinds removal
|
||||
try {
|
||||
Remove-ITDSolarwindsNode -ComputerName $ComputerName -Credential $Credential
|
||||
$CommentsForWorkNotes += ("`nSolarwinds: Node was removed for $ComputerName. ")
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
|
||||
$CommentsForWorkNotes += ("`n `nWindows OS Decommission completed. $hardware_platform $hardware_type hardware is ready for removal. ")
|
||||
$HardwareRemovalDescription = ("$ComputerName $hardware_platform $hardware_type hardware is ready for removal.")
|
||||
|
||||
Write-Verbose -Message "Add final comments for work notes into the SCTask, update the short_description for next workflow step"
|
||||
Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{
|
||||
work_notes = $CommentsForWorkNotes;
|
||||
short_description = $HardwareRemovalDescription;
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# figure out where to put it
|
||||
$x = Get-ADComputer -Filter * -Server itdk12dc7.k12.nd.us -Credential $K12Cred -Properties CanonicalName
|
||||
|
||||
|
||||
|
||||
# enter this on the machine itself
|
||||
$Credential = Get-Credential
|
||||
$AddComputerParams = @{
|
||||
DomainName = 'k12.nd.us';
|
||||
OUPath = 'OU=Prod,OU=All-General,OU=Computers,OU=ITD,DC=k12,DC=nd,DC=us' ;
|
||||
Credential = $Credential;
|
||||
}
|
||||
Add-Computer @AddComputerParams
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
$Step1Complete = $null
|
||||
$SCTaskNum = ''
|
||||
|
||||
try {
|
||||
$FQDN = 'itdcndhh22ywt.nd.gov'
|
||||
$CIDR = '10.221.10.96/28'
|
||||
|
||||
New-ITDIbDNSRecordNextAvailableIP -Hostname $FQDN -CIDR $CIDR -Credential $RadiusCred
|
||||
Write-Verbose -Message "New-ITDWindowsVmVMwareStep1 function completed"
|
||||
$Step1Complete = $true
|
||||
}
|
||||
catch {
|
||||
#Write-Error -Message $error[0]
|
||||
$Step1Complete = $false
|
||||
|
||||
|
||||
$ErrorText = ($_.ErrorDetails.message | ConvertFrom-Json).text
|
||||
If ($ErrorText -match "Cannot find 1 available IP address" ) {
|
||||
$Msg = "Resolve the issue and resubmit the Server Build Request catalog item. Setting $SCTaskNum State to Closed Incomplete"
|
||||
Write-Warning -Message $Msg
|
||||
Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{ #>
|
||||
work_notes = ("VMware build step 1 errored. $Msg. `nPSU Job Id #" + '52928' + " `n" + $ErrorText)
|
||||
state = 'Closed Incomplete'
|
||||
close_notes = ("VMware build step 1 errored. $Msg. `nPSU Job Id #" + '52928' + " `n" + $ErrorText)
|
||||
}
|
||||
|
||||
}
|
||||
throw
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.Infra-ActiveDirectory.Object'
|
||||
|
||||
variables:
|
||||
major: 0
|
||||
minor: 1
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.Infra-ActiveDirectory.Object.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.Infra-ActiveDirectory.Object.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.Infra-ActiveDirectory.Object'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.Infra-ActiveDirectory.Object).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.Infra-ActiveDirectory.Object</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>Functions for Windows Server Active Directory User, Group, and Computer tasks</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
#
|
||||
# Module manifest for module 'ITD.Windows'
|
||||
#
|
||||
# Generated by: zmeier
|
||||
#
|
||||
# Generated on: 6/14/2022
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'ITD.Infra-ActiveDirectory.Object.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = 'bfe6483c-14b2-42e9-8d98-62482eb4569c'
|
||||
|
||||
# Author of this module
|
||||
Author = 'zmeier'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'State of North Dakota'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) zmeier. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'Functions for Active Directory object (users and computers) administration'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#Get public and private function definition files.
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
|
||||
|
||||
#Dot source the files
|
||||
Foreach($import in @($Public + $Private))
|
||||
{
|
||||
Try
|
||||
{
|
||||
. $import.fullname
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Here I might...
|
||||
# Read in or create an initial config file and variable
|
||||
# Export Public functions ($Public.BaseName) for WIP modules
|
||||
# Set variables visible to the module and its functions only
|
||||
|
||||
Export-ModuleMember -Function $Public.Basename
|
||||
@@ -0,0 +1,49 @@
|
||||
function Disable-ITDADUser
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[string]
|
||||
$Identity,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
Write-Verbose "Validate credentials, stop script if invalid."
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
$Credential = Get-Credential -Message "Enter domain/OU administrator credentials. User name must be entered as a SAMAccountName (DOMAIN\username) or as a User Principal Name (username@domain.com)" -UserName $Credential
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
Write-Warning "credentials missing - stopping script"
|
||||
break
|
||||
}
|
||||
If((Test-ADCredential -Credential $Credential -ErrorAction Stop) -eq $false)
|
||||
{
|
||||
Write-Warning "Invalid credentials or locked account."
|
||||
break
|
||||
}
|
||||
}
|
||||
Import-Module ActiveDirectory
|
||||
}
|
||||
Process
|
||||
{
|
||||
$OUdestination = "OU=DisabledAccounts,OU=USERS,OU=ITD,DC=ND,DC=GOV"
|
||||
ForEach($username in $Identity)
|
||||
{
|
||||
|
||||
Write-Verbose "[$Username]:Processing"
|
||||
$object = Get-ADUser -Identity $username
|
||||
Write-Verbose "[$Username]:Disabling Object"
|
||||
$object | Set-ADuser -Enabled $false -Credential $Credential
|
||||
Write-Verbose "[$Username]:Moving Object"
|
||||
$object | Move-ADObject -TargetPath $OUdestination -Credential $Credential
|
||||
}
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
function Get-ITDADActiveServer
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[int]
|
||||
$ExpireAgeDays = 30
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
Import-Module ActiveDirectory
|
||||
$OUsource = "OU=ITD,DC=ND,DC=GOV"
|
||||
$ExpireDate = (Get-Date).AddDays((-$ExpireAgeDays))
|
||||
}
|
||||
Process
|
||||
{
|
||||
Get-ADComputer -SearchBase $OUsource -Filter * -Properties Name,CanonicalName,operatingSystem,operatingSystemServicePack,LastLogonDate,Enabled | `
|
||||
Where-Object operatingSystem -Like "*Server*" | `
|
||||
Where-Object LastLogonDate -GT $ExpireDate | `
|
||||
Where-Object Enabled -EQ $true | `
|
||||
Select-Object Name,operatingSystem,operatingSystemServicePack,LastLogonDate,CanonicalName
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Short description
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
.EXAMPLE
|
||||
Example of how to use this cmdlet
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
#>
|
||||
function Get-ITDADGroupMember
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[string]
|
||||
$Identity
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
}
|
||||
Process
|
||||
{
|
||||
return (Get-ADUser -Identity $Identity -Properties MemberOf).MemberOf
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Creates AD Computer object in ITD OUs
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
.EXAMPLE
|
||||
Example of how to use this cmdlet
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
#>
|
||||
function New-ITDADComputerServer
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[string[]]
|
||||
$ComputerName,
|
||||
|
||||
#[string]
|
||||
#$AppName,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
|
||||
Write-Verbose "Validate credentials, stop script if invalid."
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
$Credential = Get-Credential -Message "Enter domain/OU administrator credentials. User name must be entered as a SAMAccountName (DOMAIN\username) or as a User Principal Name (username@domain.com)" -UserName $Credential
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
Write-Warning "credentials missing - stopping script"
|
||||
break
|
||||
}
|
||||
If((Test-ADCredential -Credential $Credential -ErrorAction Stop) -eq $false)
|
||||
{
|
||||
Write-Warning "Invalid credentials or locked account."
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Import-Module ActiveDirectory
|
||||
$OUdefault = "OU=Prod,OU=All-General,OU=Windows,OU=SERVERS,OU=COMPUTERS,OU=ITD,DC=ND,DC=GOV"
|
||||
}
|
||||
Process
|
||||
{
|
||||
ForEach($c in $ComputerName)
|
||||
{
|
||||
$Hostname=($c.split(".")[0]).ToUpper()
|
||||
#If($AppName)
|
||||
#{
|
||||
|
||||
#}
|
||||
#Else
|
||||
#{
|
||||
$OUdestination = $OUdefault
|
||||
#}
|
||||
|
||||
New-ADComputer -Name $Hostname -Path $OUdestination -Credential $Credential
|
||||
}
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Create AD group within ITD GROUPS OU
|
||||
.DESCRIPTION
|
||||
Create Active Directory group within the ITD\ITD GROUPS OU, ability to add group members if needed
|
||||
.EXAMPLE
|
||||
New-ITDADGroup -SamAccountName ITD-GROUP-1 -Description "Sales group"
|
||||
.EXAMPLE
|
||||
New-ITDADGroup -SamAccountName ITD-GROUP-1 -Description "Sales group" -Members username1,username2,username3
|
||||
#>
|
||||
function New-ITDADGroup
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$SamAccountName,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$Description,
|
||||
|
||||
[string[]]
|
||||
$Members,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
Write-Verbose "Validate credentials, stop script if invalid."
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
$Credential = Get-Credential -Message "Enter domain/OU administrator credentials. User name must be entered as a SAMAccountName (DOMAIN\username) or as a User Principal Name (username@domain.com)" -UserName $Credential
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
Write-Warning "credentials missing - stopping script"
|
||||
break
|
||||
}
|
||||
If((Test-ADCredential -Credential $Credential -ErrorAction Stop) -eq $false)
|
||||
{
|
||||
Write-Warning "Invalid credentials or locked account."
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Import-Module ActiveDirectory
|
||||
}
|
||||
Process
|
||||
{
|
||||
Write-Verbose "verify group object does not already exist, if it does, stop script"
|
||||
$groupexists = Get-ADGroup -Filter {sAMAccountName -eq $SamAccountName}
|
||||
If($groupexists)
|
||||
{
|
||||
Write-Warning "$SamAccountName already exists"
|
||||
break
|
||||
}
|
||||
|
||||
Write-Verbose "fix description if needed"
|
||||
If($Description -like "*1120*")
|
||||
{
|
||||
Write-Verbose "no change to description"
|
||||
}
|
||||
Else
|
||||
{
|
||||
Write-Verbose "adding '1120 - ' to description"
|
||||
$Description = "1120 - " + $Description
|
||||
}
|
||||
|
||||
$OUdestination = "OU=ITDGROUPS,OU=GROUPS,OU=ITD,DC=ND,DC=GOV"
|
||||
|
||||
Write-Verbose "create group in AD"
|
||||
New-ADGroup -Name $SamAccountName `
|
||||
-SamAccountName $SamAccountName `
|
||||
-Description $Description `
|
||||
-DisplayName $SamAccountName `
|
||||
-GroupScope Global `
|
||||
-GroupCategory Security `
|
||||
-Path $OUdestination `
|
||||
-Credential $Credential
|
||||
|
||||
Write-Verbose "Adding group members if applicable"
|
||||
If($Members)
|
||||
{
|
||||
Add-ADGroupMember -Identity $SamAccountName -Members $Members -Credential $Credential
|
||||
}
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
A longer description of the function, its purpose, common use cases, etc.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
Test-MyTestFunction
|
||||
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
|
||||
#>
|
||||
|
||||
function New-ITDADServiceAccount {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$SamAccountName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$Description,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('Office365', 'VMware_Systems', 'CSRC', 'Shared Linux Password List', 'Peoplesoft Share PW', 'Cohesity', 'VDI')]
|
||||
[string]
|
||||
$PasswordstateList,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$PasswordstateTitle,
|
||||
|
||||
[string]
|
||||
$PasswordstateNotes,
|
||||
|
||||
[pscredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
Write-Verbose -Message "Verify if user object already exists in Active Directory"
|
||||
try {
|
||||
If (Get-ADUser -Identity $SamAccountName) {
|
||||
$ADUserExists = $true
|
||||
}
|
||||
}
|
||||
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
|
||||
Write-Verbose -Message "Active Directory user object not found"
|
||||
$ADUserExists = $false
|
||||
}
|
||||
catch {
|
||||
Write-Error -Message "Unable to validate if samaccountname $SamAccountName is available" -ErrorAction $Stop
|
||||
}
|
||||
|
||||
Write-Verbose -Message "ADUser exists $ADUserExists"
|
||||
|
||||
switch ($ADUserExists) {
|
||||
Default {
|
||||
Write-Error -Message "Unable to validate if samaccountname $SamAccountName is available"
|
||||
}
|
||||
$true {
|
||||
Write-Error -Message "AD user object with $SamAccountName SamAccountName already exists."
|
||||
}
|
||||
$false {
|
||||
Write-Verbose -Message "Create Passwordstate record"
|
||||
$NewITDPasswordParams = @{
|
||||
PasswordList = $PasswordstateList;
|
||||
Title = $PasswordstateTitle;
|
||||
Description = $Description;
|
||||
UserName = ("ndgov\$SamAccountName");
|
||||
Credential = $Credential;
|
||||
}
|
||||
|
||||
switch ($PSBoundParameters.Keys) {
|
||||
PasswordStateNotes {
|
||||
$NewITDPasswordParams.Notes = $PasswordstateNotes
|
||||
}
|
||||
}
|
||||
|
||||
$NewITDPasswordResult = New-ITDPassword @NewITDPasswordParams -ErrorAction Stop
|
||||
|
||||
If ($NewITDPasswordResult) {
|
||||
Write-Verbose -Message "Create AD account"
|
||||
$OuDestination = "OU=ITD SERVICE,OU=USERS,OU=ITD,DC=ND,DC=GOV"
|
||||
|
||||
$DCtoUse = Get-ADDomainController -DomainName nd.gov -Discover -Site "Default-First-Site-Name"
|
||||
|
||||
$NewADUserParams = @{
|
||||
Name = $SamAccountName;
|
||||
SamAccountName = $SamAccountName;
|
||||
UserPrincipalName = "$SamAccountName@nd.gov";
|
||||
Description = "1120 - $Description";
|
||||
Surname = "$SamAccountName";
|
||||
DisplayName = "$SamAccountName";
|
||||
Path = $OuDestination;
|
||||
AccountPassword = $NewITDPasswordResult.Password;
|
||||
PasswordNeverExpires = $true;
|
||||
Enabled = $true;
|
||||
Credential = $Credential;
|
||||
Server = $DCtoUse;
|
||||
}
|
||||
|
||||
#try {
|
||||
Write-Verbose -Message "Attempt New-ADUser"
|
||||
New-ADUser @NewADUserParams
|
||||
#}
|
||||
#catch {
|
||||
#Write-Error $error[0]
|
||||
#}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Create new account with random password, save in Passwordstate
|
||||
.DESCRIPTION
|
||||
Create new Active Directory user account in the "ITD SERVICE" OU, randomly generate a password, and save it in Passwordstate
|
||||
.EXAMPLE
|
||||
New-ITDADServiceAccount -SamAccountName !itdtest01 -Description "app/sql db account" -ComputerName itdtest01.nd.gov -PasswordStateList CSRC -Credential <PSCredential>
|
||||
#>
|
||||
function New-ITDADServiceAccountOLD
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$SamAccountName,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$Description,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateSet("CSRC","CND","Linux","Office365","VMware","ZTEST")]
|
||||
[string]
|
||||
$PasswordstateList,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
Write-Verbose "Validate credentials, stop script if invalid."
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
$Credential = Get-Credential -Message "Enter domain/OU administrator credentials. User name must be entered as a SAMAccountName (DOMAIN\username) or as a User Principal Name (username@domain.com)" -UserName $Credential
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
Write-Warning "credentials missing - stopping script"
|
||||
break
|
||||
}
|
||||
If((Test-ADCredential -Credential $Credential -ErrorAction Stop) -eq $false)
|
||||
{
|
||||
Write-Warning "Invalid credentials or locked account."
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Confirm Passwordstate connection"
|
||||
If((Test-NetConnection -ComputerName itdpv.nd.gov).PingSucceeded)
|
||||
{
|
||||
}
|
||||
Else
|
||||
{
|
||||
Write-Warning "Passwordstate unavailable"
|
||||
break
|
||||
}
|
||||
|
||||
Import-Module ActiveDirectory
|
||||
}
|
||||
Process
|
||||
{
|
||||
Write-Verbose "verify user account does not already exist, if it does, stop script"
|
||||
$userexists = Get-ADUser -Filter {sAMAccountName -eq $SamAccountName}
|
||||
If($userexists)
|
||||
{
|
||||
Write-Warning "$SamAccountName already exists"
|
||||
break
|
||||
}
|
||||
|
||||
Write-Verbose "fix description if needed"
|
||||
If($Description -like "*1120*")
|
||||
{
|
||||
Write-Warning "Do not enter '1120' into the description, this will be done for you"
|
||||
Break
|
||||
|
||||
}
|
||||
|
||||
Write-Verbose "set OU, get passwordstate passwordlist information, set ADDescription"
|
||||
$OUdestination = "OU=ITD SERVICE,OU=USERS,OU=ITD,DC=ND,DC=GOV"
|
||||
$PStateList = Get-ITDPasswordstatePasswordList -Name $PasswordstateList
|
||||
$ADDescription = "1120 - " + $Description
|
||||
|
||||
<# removed 20181228
|
||||
Write-Verbose "Generate new password"
|
||||
$PasswordGenerated = New-ITDRandomPassword
|
||||
$PasswordSecured = $PasswordGenerated | ConvertTo-SecureString -AsPlainText -Force
|
||||
|
||||
Write-Verbose "add to passwordstate"
|
||||
$Date = Get-Date -UFormat "%Y/%m/%d @ %H:%M:%S"
|
||||
$Notes = "Automatically generated by $env:USERNAME on $Date"
|
||||
New-PasswordstateRecord -ListID $PStateList.ID -Title $ComputerName -Username "nd.gov\$SamAccountName" -APIkey $PStateList.APIkey -Password $PasswordGenerated -Description $Description -Notes $Notes
|
||||
#>
|
||||
|
||||
New-ITDPasswordstateRecord -Title $ComputerName -Description $ADDescription -PSList $PasswordstateList -Username $SamAccountName -GeneratePassword
|
||||
|
||||
Write-Verbose "create account in AD"
|
||||
New-ADUser -Name $SamAccountName `
|
||||
-SamAccountName $SamAccountName `
|
||||
-UserPrincipalName "$SamAccountName@nd.gov" `
|
||||
-Description $ADDescription `
|
||||
-DisplayName "$SamAccountName" `
|
||||
-Path $OUdestination `
|
||||
-AccountPassword $PasswordSecured `
|
||||
-PasswordNeverExpires $true `
|
||||
-Enabled $true `
|
||||
-Credential $Credential
|
||||
|
||||
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A short one-line action-based description, e.g. 'Tests if a function is valid'
|
||||
.DESCRIPTION
|
||||
Function will submit a ServiceNow Catalog Request of Application Server type with relevant information required for automated AD Service Account creation.
|
||||
.NOTES
|
||||
Information or caveats about the function e.g. 'This function is not supported in Linux'
|
||||
.LINK
|
||||
Specify a URI to a help page, this will show when Get-Help -Online is used.
|
||||
.EXAMPLE
|
||||
Test-MyTestFunction -Verbose
|
||||
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
|
||||
#>
|
||||
|
||||
function New-ITDADServiceAccountRitm {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$RequestedForEmail,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$SamAccountName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('nd.gov')]
|
||||
[string]
|
||||
$ADDomain,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$Description,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
#[ValidateSet('Office365', 'VMware_Systems', 'CSRC', 'Shared Linux Password List', 'Peoplesoft Share PW', 'Cohesity', 'VDI')]
|
||||
[string]
|
||||
$PasswordstateList,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$PasswordstateTitle
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
}
|
||||
|
||||
process {
|
||||
# create Application Server RITM with json
|
||||
|
||||
$AdditionalComments = "Please create a new $ADDomain Active Directory service account with the following details, following guidelines found in KB0016867.`n`n"
|
||||
|
||||
$obj = [PSCustomObject]@{
|
||||
RequestedForEmail = $RequestedForEmail
|
||||
SamAccountName = $SamAccountName;
|
||||
ADDomain = $ADDomain;
|
||||
PasswordstateTitle = $PasswordstateTitle;
|
||||
PasswordstateList = $PasswordstateList;
|
||||
Description = $Description;
|
||||
}
|
||||
|
||||
$AdditionalComments += ($obj | ConvertTo-Json -Compress)
|
||||
|
||||
$NewITDServiceNowServiceCatalogRequest = @{
|
||||
CategoryItemName = 'Application Server';
|
||||
RequestedForEmail = $RequestedForEmail;
|
||||
Values = @{
|
||||
additional_comments = $AdditionalComments;
|
||||
request_type = "New";
|
||||
application_name = "Infra-ActiveDirectory.Object";
|
||||
environment = "Production";
|
||||
require_hosting_quote = 'No';
|
||||
add_change_disaster_recovery = 'No'; #>
|
||||
vm_work_needed = 'No';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$ReqResult = New-ITDServiceNowServiceCatalogRequest @NewITDServiceNowServiceCatalogRequest
|
||||
}
|
||||
|
||||
end {
|
||||
Write-Output $ReqResult
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Unlock any Active Directory Account
|
||||
.DESCRIPTION
|
||||
Unlock any Active Directory Account, verify information
|
||||
.EXAMPLE
|
||||
Unlock-ITDADAccount -Identity username1
|
||||
.EXAMPLE
|
||||
Unlock-ITDADAccount -Identity username1, username2, username3
|
||||
.EXAMPLE
|
||||
Unlock-ITDADAccount -Identity username1 -Credential $PSCredential
|
||||
.INPUTS
|
||||
Inputs to this cmdlet (if any)
|
||||
.OUTPUTS
|
||||
Output from this cmdlet (if any)
|
||||
.NOTES
|
||||
General notes
|
||||
.COMPONENT
|
||||
The component this cmdlet belongs to
|
||||
.ROLE
|
||||
The role this cmdlet belongs to
|
||||
.FUNCTIONALITY
|
||||
The functionality that best describes this cmdlet
|
||||
#>
|
||||
function Unlock-ITDADAccount
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string[]]
|
||||
$Identity,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
Write-Verbose "Validate credentials, stop script if invalid."
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
$Credential = Get-Credential -Message "Enter domain/OU administrator credentials. User name must be entered as a SAMAccountName (DOMAIN\username) or as a User Principal Name (username@domain.com)" -UserName $Credential
|
||||
If($Credential -eq "" -or $Credential -eq $null)
|
||||
{
|
||||
Write-Warning "credentials missing - stopping script"
|
||||
break
|
||||
}
|
||||
If((Test-ADCredential -Credential $Credential -ErrorAction Stop) -eq $false)
|
||||
{
|
||||
Write-Warning "Invalid credentials or locked account."
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
.3
|
||||
Import-Module ActiveDirectory
|
||||
}
|
||||
Process
|
||||
{
|
||||
ForEach ($i in $Identity)
|
||||
{
|
||||
$before = Get-ADUser -Identity $i -Properties SamAccountName,PasswordLastSet,lastLogonDate,Enabled,LockedOut | Select-Object SamAccountName,PasswordLastSet,lastLogonDate,Enabled,LockedOut
|
||||
$SamAccountName = $before.SamAccountName
|
||||
If($before.LockedOut -eq $false)
|
||||
{
|
||||
Write-Warning "[$SamAccountName]:Before:$before"
|
||||
}
|
||||
Else
|
||||
{
|
||||
Unlock-ADAccount -Identity $i -Credential $Credential
|
||||
$after = Get-ADUser -Identity $i -Properties SamAccountName,PasswordLastSet,lastLogonDate,Enabled,LockedOut | Select-Object SamAccountName,PasswordLastSet,lastLogonDate,Enabled,LockedOut
|
||||
Write-Warning "[$SamAccountName]:After:$after"
|
||||
}
|
||||
}
|
||||
}
|
||||
End
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Introduction
|
||||
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
|
||||
|
||||
# Getting Started
|
||||
TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
|
||||
1. Installation process
|
||||
2. Software dependencies
|
||||
3. Latest releases
|
||||
4. API references
|
||||
|
||||
# Build and Test
|
||||
TODO: Describe and show how to build your code and run the tests.
|
||||
|
||||
# Contribute
|
||||
TODO: Explain how other users and developers can contribute to make your code better.
|
||||
|
||||
If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
|
||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$SCTaskNum,
|
||||
|
||||
[switch]
|
||||
$Quiet
|
||||
)
|
||||
|
||||
begin {
|
||||
$StartTime = Get-Date
|
||||
New-ITDServiceNowSession -Environment Production -Credential $Secret:SNowVMCred
|
||||
}
|
||||
|
||||
process {
|
||||
$SCTaskSearch = Get-ITDServiceNowRecord -ItemType 'Catalog Task' -Filter ('active=true^short_description=Active Directory Service Account Provisioning') -Verbose
|
||||
switch ($PSBoundParameters.Keys) {
|
||||
'SCTaskNum' {
|
||||
$SCTaskSearch = $SCTaskSearch | Where-Object Number -EQ $SCTaskNum
|
||||
}
|
||||
}
|
||||
|
||||
Switch (@($SCTaskSearch).count) {
|
||||
{ $_ -le 0 } {
|
||||
Write-Verbose -Message "No Active Directory Service Account Provisioning tasks found." -Verbose
|
||||
}
|
||||
{ $_ -ge 1 } {
|
||||
Write-Verbose -Message ("Number of Active Directory Service Account Provisioning tasks found: " + @($SCTaskSearch).count) -Verbose
|
||||
}
|
||||
}
|
||||
|
||||
ForEach ($SCTask in $SCTaskSearch) {
|
||||
Clear-Variable -Name RITM, obj, NewITDADServiceAccountParams -ErrorAction SilentlyContinue
|
||||
Write-Verbose -Message ("Start " + $SCTask.Num)
|
||||
$Ritm = Get-ITDServiceNowRecord -ItemType 'Request Item' -SysId $SCTask.request_item.value -IncludeCustomVariable
|
||||
$RitmRequestedFor = Get-ITDServiceNowUser -SysId $Ritm.requested_for.value
|
||||
|
||||
$obj = ($Ritm.CustomVariable.additional_comments.Value -split "`n")[2] | ConvertFrom-Json
|
||||
|
||||
If ($Obj.ADDomain -ne 'nd.gov') {
|
||||
Write-Error -Message "Only nd.gov is supported, create account manually" -ErrorAction Stop
|
||||
}
|
||||
|
||||
$NewITDADServiceAccountParams = @{
|
||||
SamAccountName = $obj.SamAccountName;
|
||||
Description = $obj.Description;
|
||||
PasswordstateList = $obj.PasswordstateList;
|
||||
PasswordstateTitle = $obj.PasswordstateTitle;
|
||||
PasswordstateNotes = ("Requested via " + $RITM.number)
|
||||
Credential = $PrvCred; #$Secret:svcitdiaasauto;
|
||||
}
|
||||
try {
|
||||
New-ITDADServiceAccount @NewITDADServiceAccountParams -Verbose -ErrorAction Stop
|
||||
$Notes = "New Active Directory account created."
|
||||
$AccountCreated = $true
|
||||
}
|
||||
catch [Microsoft.PowerShell.Commands.WriteErrorException] {
|
||||
Write-Error -Message $error[0]
|
||||
$AccountCreated = $false
|
||||
}
|
||||
|
||||
|
||||
$EndTime = Get-Date
|
||||
|
||||
If ($PSBoundParameters.ContainsKey('Quiet') -and $Quiet -eq $true) {
|
||||
Write-Verbose -Message "Quiet mode enabled. No ServiceNow interactions will be done." -Verbose
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Quiet mode disabled. ServiceNow CHG will be generated." -Verbose
|
||||
# create std chg and close it
|
||||
switch ($AccountCreated) {
|
||||
$true {
|
||||
Write-Verbose -Message "AccountCreated true" -Verbose
|
||||
Write-Verbose -Message "Generating SNow CHG" -Verbose
|
||||
|
||||
#New-ITDServiceNowSession -Environment Test -Credential $Secret:SNowVMCred
|
||||
$NewITDServiceNowChangeRequestParams = @{
|
||||
TemplateName = 'NDIT-SPS-Server Add/Chg/Del'
|
||||
RequestedByUsername = $RitmRequestedFor.user_name;
|
||||
Category = 'Systems Platforms - Systems';
|
||||
Subcategory = 'Windows';
|
||||
Impact = 3;
|
||||
ShortDescription = "New nd.gov Active Directory service account created - $UAJobId, " + $RITM.number;
|
||||
Description = "New nd.gov Active Directory service account created";
|
||||
Justification = "New nd.gov Active Directory service account required for zero-trust policies, following guidelines found in KB0016867";
|
||||
Implementation = "PSUniversal execution";
|
||||
RiskImpactAnalysis = "Low";
|
||||
BackoutPlan = "Delete the new user account"
|
||||
TestPlan = "n/a"
|
||||
WhoIsImpacted = "Windows System Administrators";
|
||||
StartTime = $StartTime
|
||||
EndTime = $EndTime;
|
||||
AssignmentGroup = 'NDIT-Computer Systems Windows';
|
||||
ChangeManagerUsername = 'khellman';
|
||||
ChangeCoordinatorUsername = 'gpgolberg';
|
||||
AssignedToUsername = $RitmRequestedFor.user_name;
|
||||
}
|
||||
|
||||
$CHG = New-ITDServiceNowChangeRequest @NewITDServiceNowChangeRequestParams -Verbose
|
||||
|
||||
Update-ITDServiceNowRecord -ItemType "Change Request" -Number $CHG.Number.Value -Values @{
|
||||
work_notes = $Notes;
|
||||
}
|
||||
|
||||
Write-Verbose -Message ("Completing SNow " + $CHG.Number.value) -Verbose
|
||||
$CompleteITDServiceNowChangeRequestParams = @{
|
||||
Number = $CHG.Number.value
|
||||
CloseCode = "Successful"
|
||||
CloseNotes = ("New nd.gov Active Directory account " + $obj.ADDomain + "\" + $obj.SamAccountName + " created.")
|
||||
}
|
||||
Complete-ITDServiceNowChangeRequest @CompleteITDServiceNowChangeRequestParams -Verbose
|
||||
|
||||
New-ITDServiceNowSession -Environment Production -Credential $Secret:SNowVMCred
|
||||
|
||||
Write-Verbose -Message ("SCTASK " + $SCTask.Num + " success notes")
|
||||
Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTask.Number -Values @{
|
||||
work_notes = $Notes + "`n" + ($Chg.Number.value + " created for the work.");
|
||||
close_notes = $Notes;
|
||||
state = "Closed Complete";
|
||||
}
|
||||
}
|
||||
$false {
|
||||
Write-Verbose -Message "AccountCreated false" -Verbose
|
||||
Write-Verbose -Message ("SCTASK " + $SCTask.Num + " failure notes")
|
||||
$Message = "Error during account creation, requires human review. PSU Job Id #$UAJobId"
|
||||
Write-Warning -Message $Message
|
||||
Write-Verbose -Message ("Update " + $SCTask.Number)
|
||||
Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCtask.Number -Values @{
|
||||
work_notes = $Message;
|
||||
short_description = $SCTask.short_description + " - HUMAN REVIEW"
|
||||
}
|
||||
}
|
||||
Default {
|
||||
Write-Verbose -Message "AccountCreated default" -Verbose
|
||||
Write-Error -Message "AccountCreated variable is somehow not true or false... not sure how that happened. Great work!"
|
||||
}
|
||||
}
|
||||
}
|
||||
Write-Verbose -Message ("End " + $SCTask.Num)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$RequestedForEmail,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$SamAccountName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('nd.gov')]
|
||||
[string]
|
||||
$ADDomain,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$Description,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('Office365', 'VMware_Systems', 'CSRC', 'Shared Linux Password List', 'Peoplesoft Share PW', 'Cohesity', 'VDI')]
|
||||
[string]
|
||||
$PasswordstateList,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$PasswordstateTitle
|
||||
)
|
||||
|
||||
$NewITDADServiceAccountParams = @{
|
||||
RequestedForEmail = $RequestedForEmail;
|
||||
SamAccountName = $SamAccountName;
|
||||
ADDomain = $ADDomain;
|
||||
Description = $Description;
|
||||
PasswordstateList = $PasswordstateList;
|
||||
PasswordstateTitle = $PasswordstateTitle;
|
||||
}
|
||||
|
||||
New-ITDServiceNowSession -Environment Production -Credential $Secret:SNowVMCred -Verbose
|
||||
New-ITDADServiceAccountRitm @NewITDADServiceAccountParams -Verbose
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$SamAccountName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$Description,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('VMware_Systems', 'CSRC', 'Shared Linux Password List', 'Peoplesoft Share PW', 'Cohesity', 'VDI', 'Office365')]
|
||||
[string]
|
||||
$PasswordstateList,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$PasswordstateTitle,
|
||||
|
||||
[switch]
|
||||
$Quiet
|
||||
)
|
||||
|
||||
$StartTime = Get-Date
|
||||
|
||||
$NewITDADServiceAccountParams = @{
|
||||
SamAccountName = $SamAccountName;
|
||||
Description = $Description;
|
||||
PasswordstateList = $PasswordstateList;
|
||||
PasswordstateTitle = $PasswordstateTitle;
|
||||
Credential = $Secret:svcitdiaasauto;
|
||||
}
|
||||
try {
|
||||
New-ITDADServiceAccount @NewITDADServiceAccountParams -Verbose
|
||||
}
|
||||
catch {
|
||||
Write-Error -Message $error[0] -ErrorAction Stop
|
||||
}
|
||||
|
||||
|
||||
$EndTime = Get-Date
|
||||
If ($PSBoundParameters.ContainsKey('Quiet') -and $Quiet -eq $true) {
|
||||
Write-Verbose -Message "Quiet mode enabled. No ServiceNow interactions will be done." -Verbose
|
||||
}
|
||||
Else {
|
||||
Write-Verbose -Message "Quiet mode disabled. ServiceNow CHG will be generated." -Verbose
|
||||
# create std chg and close it
|
||||
New-ITDServiceNowSession Test -Credential $Secret:SNowVMCred
|
||||
$NewITDServiceNowChangeRequestParams = @{
|
||||
TemplateName = 'NDIT-SPS-Server Add/Chg/Del'
|
||||
RequestedByUsername = 'zmeier';
|
||||
Category = 'Systems Platforms - Systems';
|
||||
Subcategory = 'Windows';
|
||||
Impact = 3;
|
||||
ShortDescription = "New nd.gov Active Directory service account created - $UAJobId";
|
||||
Description = "New nd.gov Active Directory service account created";
|
||||
Justification = "New nd.gov Active Directory service account required for zero-trust policies";
|
||||
Implementation = "PSUniversal execution";
|
||||
RiskImpactAnalysis = "Low";
|
||||
BackoutPlan = "Delete the new user account"
|
||||
TestPlan = "n/a"
|
||||
WhoIsImpacted = "Windows System Administrators";
|
||||
StartTime = $StartTime
|
||||
EndTime = $EndTime;
|
||||
AssignmentGroup = 'NDIT-Computer Systems Windows';
|
||||
ChangeManagerUsername = 'khellman';
|
||||
ChangeCoordinatorUsername = 'gpgolberg';
|
||||
AssignedToUsername = 'zmeier';
|
||||
}
|
||||
|
||||
$CHG = New-ITDServiceNowChangeRequest @NewITDServiceNowChangeRequestParams -Verbose
|
||||
|
||||
Update-ITDServiceNowRecord -ItemType "Change Request" -Number $CHG.Number.Value -Values @{
|
||||
work_notes = $Notes;
|
||||
}
|
||||
|
||||
Complete-ITDServiceNowChangeRequest -Number $CHG.Number.value -CloseCode "Successful" -CloseNotes "New nd.gov Active Directory account ndgov\$SamAccountName created." -Verbose
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
New-UDApp -Title 'SyncVMwareVMtoSharePoint' -Pages @(
|
||||
New-UDPage -Name "Home" -Content {
|
||||
New-UDForm -Content {
|
||||
New-UDTypography -Text 'Enter the information below (all fields required) and click the Submit button'
|
||||
New-UDRow -Columns {
|
||||
New-UDColumn -SmallSize 6 -LargeSize 6 -Content {
|
||||
New-UDTextbox -Label 'RequestedForEmail' -Id RequestedForEmail
|
||||
New-UDTextbox -Label 'ADDomain' -Id ADDomain
|
||||
New-UDTextbox -Label 'SamAccountName' -Id SamAccountName
|
||||
New-UDTextbox -Label 'Description' -Id Description
|
||||
New-UDTextbox -Label 'PasswordstateList' -Id PasswordstateList
|
||||
New-UDTextbox -Label 'PasswordstateTitle' -Id PasswordstateTitle
|
||||
}
|
||||
}
|
||||
} -OnValidate {
|
||||
$FormContent = $EventData
|
||||
|
||||
if ($EventData.RequestedForEmail -eq $null -or $EventData.RequestedForEmail -eq '' -or $EventData.ADDomain -eq $null -or $EventData.ADDomain -eq '' -or $EventData.SamAccountName -eq $null -or $EventData.SamAccountName -eq '' -or $EventData.Description -eq $null -or $EventData.Description -eq '' -or $EventData.PasswordstateList -eq $null -or $EventData.PasswordstateList -eq '' -or $EventData.PasswordstateTitle -eq $null -or $EventData.PasswordstateTitle -eq '') {
|
||||
# ('Office365', 'VMware_Systems', 'CSRC', 'Shared Linux Password List', 'Peoplesoft Share PW', 'Cohesity', 'VDI')
|
||||
New-UDFormValidationResult -ValidationError "All fields are required."
|
||||
}
|
||||
else {
|
||||
if ($FormContent.ADDomain -ne 'nd.gov') {
|
||||
New-UDFormValidationResult -ValidationError "Only nd.gov ADDomain is supported at this time."
|
||||
}
|
||||
else {
|
||||
If (@('Office365', 'VMware_Systems', 'CSRC', 'Shared Linux Password List', 'Peoplesoft Share PW', 'Cohesity', 'VDI') -notcontains $EventData.PasswordstateList) {
|
||||
New-UDFormValidationResult -ValidationError "PasswordstateList must match one of the following: VMware_Systems, Cohesity, VDI, Office365, CSRC, Shared Linux Password List, Peoplesoft Share PW"
|
||||
}
|
||||
}
|
||||
New-UDFormValidationResult -Valid
|
||||
}
|
||||
} -OnSubmit {
|
||||
$InvokePSUScriptParams = @{
|
||||
Script = 'New-ITDADServiceAccountRitm_script.ps1';
|
||||
RequestedForEmail = $EventData.RequestedForEmail
|
||||
ADDomain = $EventData.ADDomain
|
||||
SamAccountName = $EventData.SamAccountName
|
||||
Description = $EventData.Description
|
||||
PasswordstateList = $EventData.PasswordstateList
|
||||
PasswordstateTitle = $EventData.PasswordstateTitle
|
||||
|
||||
}
|
||||
$InvokePSUScriptResult = Invoke-PSUScript @InvokePSUScriptParams -Wait
|
||||
Show-UDToast -Message ("ServiceNow Request " + $InvokePSUScriptResult.number + " created on behalf of $User" ) -Duration 100000
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.Infra-Certificate-External.Sectigo'
|
||||
|
||||
variables:
|
||||
major: 0
|
||||
minor: 1
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.Infra-Certificate-External.Sectigo.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.Infra-Certificate-External.Sectigo.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.Infra-Certificate-External.Sectigo'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.Infra-Certificate-External.Sectigo).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"AppConfig": {
|
||||
|
||||
"AuthBaseAPIUrl": {
|
||||
"Description": "API url for Sectigo - Authentication only",
|
||||
"Value": "https://auth.sso.sectigo.com"
|
||||
},
|
||||
|
||||
"BaseAPIUrl": {
|
||||
"Description": "API url for Sectigo - Calls",
|
||||
"Value": "https://admin.hard.sectigo.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.Infra-Certificate-External.Sectigo</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Freeman Peterson</authors>
|
||||
<description>Functions for Sectigo certificate enrollment and deployment</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
#
|
||||
# Module manifest for module 'ITD.Infra-Certificate-External.Sectigo'
|
||||
#
|
||||
# Generated by: zmeier
|
||||
#
|
||||
# Generated on: 11/5/2025
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'ITD.Infra-Certificate-External.Sectigo.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
|
||||
# Supported PSEditions
|
||||
CompatiblePSEditions = 'Desktop', 'Core'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '5c65f13f-9bde-40d3-97ce-aa9b37883db2'
|
||||
|
||||
# Author of this module
|
||||
Author = 'fjpeterson'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'State of North Dakota'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) 2026 fjpeterson. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'Functions for Sectigo certificate enrollment and deployment'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
#Get public and private function definition files.
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
|
||||
|
||||
#Dot source the files
|
||||
Foreach($import in @($Public + $Private))
|
||||
{
|
||||
Try
|
||||
{
|
||||
. $import.fullname
|
||||
}
|
||||
Catch
|
||||
{
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Here I might...
|
||||
# Read in or create an initial config file and variable
|
||||
# Export Public functions ($Public.BaseName) for WIP modules
|
||||
# Set variables visible to the module and its functions only
|
||||
|
||||
Export-ModuleMember -Function $Public.Basename
|
||||
@@ -0,0 +1,37 @@
|
||||
# Using configuration file for most of the settings
|
||||
$ConfigFile="${PSScriptRoot}/../Data/config.json"
|
||||
$Config=(Get-Content -Path $ConfigFile|ConvertFrom-Json).AppConfig
|
||||
|
||||
#Load Configuration File as Variables
|
||||
$ConfigVars=($Config | Get-Member -Membertype Noteproperty).Name
|
||||
ForEach ($ConfigVar in $ConfigVars) {
|
||||
Set-Variable -Name $ConfigVar -Value $Config.${ConfigVar}.Value
|
||||
}
|
||||
|
||||
#Remove temporary variables we don't need to reused thse
|
||||
Remove-Variable -Name Config,ConfigFile,ConfigVar,ConfigVars
|
||||
|
||||
$TimeZone=(timezone).id
|
||||
|
||||
# We need to declare the fields for syncing.
|
||||
$SyncFields = @("Admins", "AgencyName","App","City","Cluster","Environment","SRM_Request","SRM_Status","SupportHours","TeamLead","VLAN","VMHost" )
|
||||
$SNSyncFields=@("Environment","AgencyName")
|
||||
|
||||
#Default before set
|
||||
$VerbosePreference = "SilentlyContinue"
|
||||
$InformationPreference = "SilentlyContinue"
|
||||
$DebugPreference = "SilentlyContinue"
|
||||
|
||||
# Sets logging levels for cmdlets.
|
||||
Switch ($VerboseLevel) {
|
||||
0 { $VerbosePreference = "SilentlyContinue" }
|
||||
1 { $VerbosePreference = "Continue" }
|
||||
2 { $VerbosePreference = "Continue" ; $InformationPreference = "Continue" }
|
||||
3 { $VerbosePreference = "Continue" ; $InformationPreference = "Continue"; $DebugPreference = "Continue" }
|
||||
}
|
||||
|
||||
# Overrides email SNMPTO
|
||||
if ($TestMode -eq $True) {
|
||||
Write-Information "Test Mode - On"
|
||||
$SNMPTo = $TestModeAdmin
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
|
||||
#'x509' - for Certificate (w/ chain), PEM encoded,
|
||||
#'x509CO' - for Certificate only, PEM encoded,
|
||||
#'base64' - for PKCS#7, PEM encoded,
|
||||
#'bin' - for PKCS#7, 'x509IO' - for Root/Intermediate(s) only, PEM encoded,
|
||||
#'x509IOR' - for Intermediate(s)/Root only, PEM encoded,
|
||||
#'pem' - for Certificate (w/ chain), PEM encoded,
|
||||
#'pemco' - for Certificate only, PEM encoded,
|
||||
#'pemia' - for Certificate (w/ issuer after), PEM encoded,
|
||||
#'x509R' - for Certificate (w/ chain), PEM encoded.
|
||||
# base64 is default.
|
||||
|
||||
function Download-SectigoCertificate {
|
||||
[CmdletBinding(SupportsShouldProcess=$true)]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken,
|
||||
[string]$CertRootPath="c:\certs",
|
||||
[ValidateSet('x509','x509CO','base64','bin','x509IOR','pem','pemco','pemia','x509R' )]
|
||||
[string]$Format="x509CO",
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$OrderId
|
||||
)
|
||||
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host "ApiToken:"
|
||||
}
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
[string]$CollectUrl = "${BaseAPIUrl}/api/ssl/v1/collect/${OrderId}?format=${format}"
|
||||
|
||||
Write-Verbose -Verbose "CollectUrl: $CollectUrl"
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json" # <-- Cleaned up syntax
|
||||
}
|
||||
|
||||
# --- API Call ---
|
||||
Write-Verbose "Attempting to retrieve certificate for Order ID: $OrderId"
|
||||
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri $CollectUrl -Method Get -Headers $headers -UseBasicParsing -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Error "API Request Failed: $($_.Exception.Message)"
|
||||
return $null
|
||||
}
|
||||
$OutPath = "${CertRootPath}\cert_${OrderId}.cer"
|
||||
|
||||
# --- Response Processing ---
|
||||
if ($response.StatusCode -eq 200) {
|
||||
Write-Verbose "Certificate successfully retrieved (Status 200)."
|
||||
|
||||
# 1. Get the Hex String
|
||||
# ASSUMPTION: The API returns the raw certificate Hex string in the response content.
|
||||
# If the API returns JSON, you must use 'ConvertFrom-Json' first to extract the hex property.
|
||||
$decimalNumbersString = $response.Content
|
||||
|
||||
$numberStrings = $decimalNumbersString -split '\s+|,|\r?\n' | Where-Object { $_ }
|
||||
|
||||
try {
|
||||
[byte[]]$bytes = $numberStrings | ForEach-Object { [int]$_ }
|
||||
} catch {
|
||||
Write-Error "Error converting numbers. Ensure all numbers are between 0 and 255."
|
||||
exit
|
||||
}
|
||||
|
||||
# Write the byte array to the binary file
|
||||
[System.IO.File]::WriteAllBytes($OutPath , $bytes)
|
||||
Get-ChildItem $OutPath|select fullname, LastWriteTime
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
Function Enroll-SectigoCertificateRequest {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken,
|
||||
[int]$OrgId=8091, # 8091 friendly label is "Information Technology Department - Windows"
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$subjAltNames,
|
||||
[ValidateSet('IIS','IIS_OLD','IBM','LINUX','Apache','Tomcat')]
|
||||
[string]$Type="IIS",
|
||||
[string]$comment = "",
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$dcvEmail,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateSet('ECC',"RSA")]
|
||||
[string]$KeyType,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$Csr, # Replace with your Sectigo Organization ID
|
||||
[switch]$Test
|
||||
)
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host "ApiToken:"
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
[string]$RequestUrl= $BaseAPIUrl + "/api/ssl/v1/enroll"
|
||||
|
||||
Write-Verbose -Verbose "RequestUrl: $RequestUrl"
|
||||
#$CertType=2369
|
||||
|
||||
#If ($subjAltNames) {
|
||||
$CertType=2375
|
||||
#}
|
||||
|
||||
|
||||
$term=365
|
||||
# 7: IBM HTTP Server
|
||||
# 14: Microsoft IIS 5 or 6
|
||||
switch ($ServerType.ToLower()) {
|
||||
"iis" {
|
||||
$ServerTypeCode = 35
|
||||
}
|
||||
"iis_old" {
|
||||
$ServerTypeCode = 14
|
||||
}
|
||||
"ibm" {
|
||||
$ServerTypeCode = 7
|
||||
}
|
||||
"linux" {
|
||||
$ServerTypeCode = 'Linux'
|
||||
}
|
||||
"apache" {
|
||||
$ServerTypeCode = 2
|
||||
}
|
||||
"tomcat" {
|
||||
$ServerTypeCode = 12
|
||||
}
|
||||
default {
|
||||
Write-Warning "Unsupported server type: $ServerType. Please provide specific instructions for manual installation."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ignorded for now
|
||||
# keySize = 2048,
|
||||
# keyParam = 2048,
|
||||
# algorithm = $KeyType
|
||||
# keyGenerationMethod = PK_AGENT
|
||||
|
||||
|
||||
$body = @{
|
||||
orgId = $OrgId
|
||||
subjAltNames = $subjAltNames
|
||||
certType = $CertType
|
||||
term = $term
|
||||
serverType = $ServerTypeCode
|
||||
comments = $comment
|
||||
csr = $csr
|
||||
externalRequester = $dcvEmail
|
||||
}
|
||||
|
||||
#$b2= @{
|
||||
# subjAltNames = $subjAltNames
|
||||
#}
|
||||
#
|
||||
#if ($subjAltNames) {
|
||||
# $body = $body + $b2
|
||||
#}
|
||||
|
||||
|
||||
|
||||
$b3=@{
|
||||
commonName = $commonName
|
||||
keySize = 2048
|
||||
keyParam = "2048"
|
||||
algorithm = "RSA"
|
||||
keyGenerationMethod = "PK_AGENT"
|
||||
}
|
||||
|
||||
$b4=@{
|
||||
commonName = $commonName
|
||||
keyParam = "secp256r1"
|
||||
algorithm = "ESS"
|
||||
keyGenerationMethod = "PK_AGENT"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# $body = $body + $b2
|
||||
#If ($KeyType -eq "rsa") {
|
||||
# $body = $body + $b3
|
||||
#} else {
|
||||
# $body = $body + $b4
|
||||
#}
|
||||
|
||||
|
||||
|
||||
|
||||
If ($test) {
|
||||
Return
|
||||
}
|
||||
|
||||
# Convert the body to JSON
|
||||
$jsonBody = $body | ConvertTo-Json
|
||||
|
||||
Write-Host $jsonBody
|
||||
|
||||
# --- Set up Authentication Headers ---
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
# --- Send the Request ---
|
||||
try {
|
||||
$response=Invoke-RestMethod -Uri $RequestUrl -Method POST -Headers $headers -Body $jsonBody -ContentType "application/json"
|
||||
|
||||
return $response
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error during certificate enrollment: $($_.Exception.Message)"
|
||||
if ($_.Exception.Response) {
|
||||
$errorResponse = $_.Exception.Response.GetResponseStream()
|
||||
$reader = New-Object System.IO.StreamReader($errorResponse)
|
||||
$responseBody = $reader.ReadToEnd()
|
||||
Write-Error "Sectigo API Error Response: $responseBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
Function Get-SectigoApiToken {
|
||||
|
||||
#[CmdletBinding()]
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
[string]$tokenEndpoint = $AuthBaseAPIUrl + "/auth/realms/apiclients/protocol/openid-connect/token"
|
||||
|
||||
$clientId=$env:Sectigoclientid
|
||||
$clientSecret=$env:SectigoclientSecret
|
||||
|
||||
if (-Not $clientid) {
|
||||
$clientid=Read-Host "Please enter your clientid"
|
||||
}
|
||||
|
||||
if (-Not $clientSecret) {
|
||||
$clientSecret=Read-Host "Please enter your clientSecret"
|
||||
}
|
||||
|
||||
Write-Verbose -Verbose "tokenEndpoint: $tokenEndpoint "
|
||||
|
||||
# Prepare the body for the token request
|
||||
$body = @{
|
||||
grant_type = "client_credentials"
|
||||
client_id = $clientId
|
||||
client_secret = $clientSecret
|
||||
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
# Request the access token
|
||||
try {
|
||||
$tokenResponse = Invoke-RestMethod -Uri $tokenEndpoint -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"
|
||||
$accessToken = $tokenResponse.access_token
|
||||
$env:SectigoToken=$accessToken
|
||||
if ($accesstoken) { Write-Verbose -Verbose 'Token Set $ENV:SectigoToken'}
|
||||
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to obtain access token: $($_.Exception.Message)"
|
||||
#exit 1
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
|
||||
function Get-SectigoCertificate {
|
||||
[CmdletBinding(SupportsShouldProcess=$true)]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$OrderId
|
||||
)
|
||||
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host "ApiToken:"
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
# [string]$dcvUrl = "${BaseAPIUrl}/api/ssl/v1/${OrderId}/dcv"
|
||||
[string]$dcvUrl = "${BaseAPIUrl}/api/ssl/v1/${OrderId}"
|
||||
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json" # <-- Cleaned up syntax
|
||||
}
|
||||
|
||||
# --- API Call ---
|
||||
Write-Verbose "Attempting to retrieve certificate for Order ID: $OrderId"
|
||||
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri $dcvUrl -Method Get -Headers $headers -UseBasicParsing -ErrorAction Stop
|
||||
return $response
|
||||
} catch {
|
||||
Write-Error "API Request Failed: $($_.Exception.Message)"
|
||||
return $null
|
||||
}
|
||||
$response
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
function Get-SectigoCertificateTypes {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken
|
||||
)
|
||||
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host "ApiToken:"
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
[string]$CertificateTypesUrl= $BaseAPIUrl + "/api/ssl/v1/types"
|
||||
Write-Verbose -Verbose "CertificateTypesUrl: $CertificateTypesUrl"
|
||||
|
||||
# 1. Prepare the Authorization Header
|
||||
# The Sectigo API usually requires the token in a Bearer authorization header.
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
# 2. Send the Request (GET is the standard method for listing resources)
|
||||
$response = Invoke-WebRequest -Uri $CertificateTypesUrl -Method Get -Headers $headers -UseBasicParsing
|
||||
|
||||
if ($response.StatusCode -eq 200) {
|
||||
# 3. Process the Response
|
||||
$certTypes = $response.Content | ConvertFrom-Json
|
||||
|
||||
# ASSUMPTION: The API returns an array of objects,
|
||||
# each representing a certificate type.
|
||||
# This function returns the entire list/array.
|
||||
return $certTypes
|
||||
|
||||
} else {
|
||||
Write-Error "Failed to get Certificate Types. Status code: $($response.StatusCode)."
|
||||
Write-Error "Response content: $($response.Content)"
|
||||
return $null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Load Configuration Variables
|
||||
|
||||
|
||||
function Get-SectigoOrg {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken
|
||||
)
|
||||
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
[string]$OrganizationLookupUrl=$BaseAPIUrl + "/api/organization/v1"
|
||||
|
||||
# 1. Prepare the Authorization Header
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
try {
|
||||
# Invoke the web request to the Sectigo API
|
||||
$Response = Invoke-WebRequest -Uri $OrganizationLookupUrl -Headers $Headers -Method GET
|
||||
|
||||
# Check if the request was successful
|
||||
if ($Response.StatusCode -eq 200) {
|
||||
# Parse the JSON response
|
||||
$Organizations = $Response.Content | ConvertFrom-Json
|
||||
$Organizations|select-object id, name
|
||||
$Organizations.departments
|
||||
|
||||
} else {
|
||||
Write-Error "Failed to retrieve organizations. Status Code: $($Response.StatusCode)"
|
||||
Write-Error "Response Content: $($Response.Content)"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the API call: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Load Configuration Variables
|
||||
|
||||
|
||||
function Get-SectigoSeverTypes {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken
|
||||
)
|
||||
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
[string]$OrganizationLookupUrl=$BaseAPIUrl + "/api/v1/servertype"
|
||||
|
||||
# 1. Prepare the Authorization Header
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
try {
|
||||
# Invoke the web request to the Sectigo API
|
||||
$Response = Invoke-WebRequest -Uri $OrganizationLookupUrl -Headers $Headers -Method GET
|
||||
|
||||
# Check if the request was successful
|
||||
if ($Response.StatusCode -eq 200) {
|
||||
# Parse the JSON response
|
||||
$Response.Content | ConvertFrom-Json
|
||||
|
||||
|
||||
} else {
|
||||
Write-Error "Failed to retrieve organizations. Status Code: $($Response.StatusCode)"
|
||||
Write-Error "Response Content: $($Response.Content)"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the API call: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
|
||||
function Revoke-SectigoCertificate {
|
||||
[CmdletBinding(SupportsShouldProcess=$true)]
|
||||
param (
|
||||
[string]$ApiToken=$env:SectigoToken,
|
||||
|
||||
[int]$reasonCode=4,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$reason,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$OrderId
|
||||
)
|
||||
|
||||
if (-Not $ApiToken) {
|
||||
$ApiToken=Read-Host
|
||||
}
|
||||
. $PSScriptRoot\..\Private\Set-Onload.ps1
|
||||
|
||||
[string]$RevokeUrl = "${BaseAPIUrl}/api/ssl/v1/revoke/${OrderId}"
|
||||
|
||||
Write-Verbose -Verbose "RequestUrl: $RevokeUrl"
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $ApiToken"
|
||||
"Content-Type" = "application/json" # <-- Cleaned up syntax
|
||||
}
|
||||
|
||||
$body = @{
|
||||
reasonCode = $reasonCode
|
||||
reason = $reasonCode
|
||||
}
|
||||
|
||||
$jsonBody = $body | ConvertTo-Json
|
||||
|
||||
# --- API Call ---
|
||||
Write-Verbose "Attempting to retrieve certificate for Order ID: $OrderId"
|
||||
|
||||
try {
|
||||
$response=Invoke-RestMethod -Uri $RevokeUrl -Method POST -Headers $headers -Body $jsonBody -ContentType "application/json"
|
||||
return $response
|
||||
"Success"
|
||||
} catch {
|
||||
Write-Error "API Request Failed: $($_.Exception.Message)"
|
||||
return $null
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
|
||||
function Test-SectigoCertificateRequest {
|
||||
[CmdletBinding(SupportsShouldProcess=$true)]
|
||||
param (
|
||||
[string]$FilePath,
|
||||
[string]$csr
|
||||
)
|
||||
|
||||
if ($FilePath) {
|
||||
[string]$csr = (Get-Content -Path $FilePath -Raw)
|
||||
}
|
||||
|
||||
[string]$dcvUrl = "https://certificates.nd.gov/api/csr/validate/string"
|
||||
|
||||
|
||||
$headers = @{
|
||||
"accept" = "application/json"
|
||||
"Content-Type" = "application/json" # <-- Cleaned up syntax
|
||||
}
|
||||
|
||||
$Body = @{
|
||||
"csr" = $csr
|
||||
}
|
||||
|
||||
$jsonBody = $body | ConvertTo-Json
|
||||
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
# --- API Call ---
|
||||
Write-Verbose "Attempting to retrieve certificate for Order ID: $OrderId"
|
||||
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri $dcvUrl -Method Post -Headers $headers -Body $jsonBody
|
||||
$response
|
||||
} catch {
|
||||
Write-Error "API Request Failed: $($_.Exception.Message)"|convertfrom-json
|
||||
return $null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
# ITD.Infra-Certificate-Internal.Sectigo
|
||||
|
||||
### Written by: Freeman Peterson fjpeterson@nd.gov
|
||||
|
||||
# Description
|
||||
This module is used to interact Sectigo API
|
||||
|
||||
## Prerequisites
|
||||
None
|
||||
|
||||
# Api Documentation
|
||||
https://www.sectigo.com/knowledge-base/detail/Sectigo-Certificate-Manager-SCM-REST-API/kA01N000000XDkE
|
||||
|
||||
# Git repo
|
||||
https://dev.azure.com/ndgov/NDIT-WindowsServerTeam/_git/ITD.Infra-Certificate-Internal.Sectigo
|
||||
|
||||
# Install
|
||||
|
||||
### Add Gallary
|
||||
```
|
||||
Register-PSRepository -Name ITD_PwshGallery `
|
||||
-SourceLocation "https://powershell.nd.gov/ITD_PwshGallery/nuget/" `
|
||||
-PublishLocation "https://powershell.nd.gov/ITD_PwshGallery/nuget/" `
|
||||
-InstallationPolicy Trusted
|
||||
```
|
||||
|
||||
### Install Module
|
||||
```
|
||||
Find-Module -Repository ITD_PwshGallery -Name ITD.Infra-Certificate-Internal.Sectigo|Install-Module -Scope CurrentUser
|
||||
```
|
||||
|
||||
### Validate Module installed
|
||||
```
|
||||
Get-Command -Module ITD.Infra-Certificate-Internal.Sectigo
|
||||
```
|
||||
### Update Module
|
||||
```
|
||||
Find-Module -Repository ITD_PwshGallery -Name ITD.Infra-Certificate-Internal.Sectigo|Update-Module
|
||||
```
|
||||
|
||||
# Examples and Information
|
||||
|
||||
### Obtaining CSR From File
|
||||
```
|
||||
$csr = (Get-Content -Path "c:\temp\hostname.csr" -Raw)
|
||||
```
|
||||
|
||||
### Get Token Prompt for Creds
|
||||
```
|
||||
$env:Sectigoclientid='b16d95fd-405f-4d41-a748-c1035916a359'
|
||||
$env:SectigoclientSecret=redacted
|
||||
|
||||
Get-SectigoApiToken
|
||||
```
|
||||
|
||||
|
||||
### Test Cert Request
|
||||
```
|
||||
Test-SectigoCertificateRequest -FilePath $csrpath
|
||||
```
|
||||
|
||||
### Enroll the CSR
|
||||
```
|
||||
$certRequest = Enroll-SectigoCertificateRequest -Csr $csr -dcvEmail 'youremail@nd.gov" #-Comment "app123"
|
||||
```
|
||||
|
||||
### Get Cert Status
|
||||
```
|
||||
Get-SectigoCertificate -Orderid $certRequest.sslid
|
||||
```
|
||||
|
||||
### Wait for approval
|
||||
```
|
||||
while ($certstatus -ne "Issued") {
|
||||
$certstatus=(Get-SectigoCertificate -OrderId $certRequest.sslid).status
|
||||
Sleep 1
|
||||
}
|
||||
```
|
||||
|
||||
### Download Cert
|
||||
```
|
||||
$CertPath = (Download-SectigoCertificate -Orderid $certRequest.sslid -Format "pem").FullName
|
||||
```
|
||||
|
||||
Default Format: Pem
|
||||
'x509' - for Certificate (w/ chain) PEM encoded
|
||||
'x509CO' - for Certificate only, PEM encoded
|
||||
'base64' - for PKCS#7, PEM encoded
|
||||
'bin' - for PKCS#7, 'x509IO' - for Root/Intermediate(s) only, PEM encoded
|
||||
'x509IOR' - for Intermediate(s)/Root only, PEM encoded
|
||||
'pem' - for Certificate (w/ chain), PEM encoded
|
||||
'pemco' - for Certificate only, PEM encoded
|
||||
'pemia' - for Certificate (w/ issuer after), PEM encoded
|
||||
'x509R' - for Certificate (w/ chain), PEM encoded
|
||||
base64
|
||||
|
||||
### Add it to a cert store
|
||||
As administrator:
|
||||
```
|
||||
Import-Certificate -FilePath "C:\Certs\cert_OrderNumber.pem" -CertStoreLocation Cert:\LocalMachine\My
|
||||
```
|
||||
|
||||
# Other functions
|
||||
|
||||
### Revoke
|
||||
```
|
||||
Revoke-SectigoCertificate -reasonCode 4 -reason "Just a test" -Orderid 11012388
|
||||
```
|
||||
No results will be given back to you.
|
||||
|
||||
*If you revoke a cert you will no longer see them in Get-SectigoCertificate
|
||||
Reason code (unspecified (0), keyCompromise (1), affiliationChanged (3), superseded (4), cessationOfOperation (5))
|
||||
|
||||
### Org Lookup
|
||||
```
|
||||
Get-SectigoOrg
|
||||
```
|
||||
|
||||
### Certificate Types
|
||||
```
|
||||
Get-SectigoCertificateTypes
|
||||
```
|
||||
+41951
File diff suppressed because one or more lines are too long
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.Infra-Networking-Infoblox'
|
||||
|
||||
variables:
|
||||
major: 1
|
||||
minor: 2
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.Infra-Networking-Infoblox.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.Infra-Networking-Infoblox.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.Infra-Networking-Infoblox'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.Infra-Networking-Infoblox).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.Infra-Networking-Infoblox</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>Functions for Infoblox use</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,10 @@
|
||||
@{
|
||||
RootModule = 'ITD.Infra-Networking-Infoblox.psm1'
|
||||
ModuleVersion = '<ModuleVersion>'
|
||||
GUID = '32fa3228-dfa3-4cc9-aac8-e14332a46abf'
|
||||
Author = 'Zack Meier'
|
||||
CompanyName = 'State of North Dakota'
|
||||
PowerShellVersion = '5.1'
|
||||
CompatiblePSEditions = 'Desktop','Core'
|
||||
FunctionsToExport = @(<FunctionsToExport>)
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Short description
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
.EXAMPLE
|
||||
Example of how to use this cmdlet
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
#>
|
||||
function Get-ITDIbDNSRecord {
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[string[]]
|
||||
$Hostname,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin {
|
||||
If (!($Credential)) {
|
||||
$Credential = Get-Credential
|
||||
}
|
||||
}
|
||||
Process {
|
||||
$result = @()
|
||||
ForEach ($h in $hostname) {
|
||||
$x = Invoke-RestMethod -Method Get -Uri "https://infoblox-gmv.ns.nd.gov/wapi/v2.7/record:host?name=$h" -ContentType "application/json" -Credential $Credential
|
||||
If ($x) {
|
||||
$obj = [PSCustomObject]@{
|
||||
'HostName' = $x.ipv4addrs.Host;
|
||||
'IPv4Address' = $x.ipv4addrs.ipv4addr
|
||||
'DHCP' = $x.ipv4addrs.configure_for_dhcp
|
||||
}
|
||||
$result += $obj
|
||||
}
|
||||
}
|
||||
}
|
||||
End {
|
||||
Write-Output $result
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Short description
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
.EXAMPLE
|
||||
Example of how to use this cmdlet
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
#>
|
||||
function New-ITDIbDNSRecord {
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ipaddress]
|
||||
$IPv4Address,
|
||||
|
||||
[string]
|
||||
$Hostname,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin {
|
||||
If (!($Credential)) {
|
||||
$Credential = Get-Credential
|
||||
}
|
||||
|
||||
$Uri = "https://infoblox-gmv.ns.nd.gov/wapi/v2.7/record:host"
|
||||
$IPs = @([PSCustomObject]@{'ipv4addr' = $IPv4Address })
|
||||
}
|
||||
Process {
|
||||
$obj = [PSCustomObject]@{
|
||||
'ipv4addrs' = $IPs;
|
||||
'name' = $Hostname;
|
||||
'view' = "default";
|
||||
}
|
||||
$postJson = $obj | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri $Uri -Method Post -Body $postJson -ContentType "application/json" -Credential $Credential
|
||||
}
|
||||
End {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-IPs {
|
||||
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[array]
|
||||
$Subnets
|
||||
)
|
||||
|
||||
foreach ($subnet in $subnets) {
|
||||
|
||||
#Split IP and subnet
|
||||
$IP = ($Subnet -split "\/")[0]
|
||||
$SubnetBits = ($Subnet -split "\/")[1]
|
||||
|
||||
#Convert IP into binary
|
||||
#Split IP into different octects and for each one, figure out the binary with leading zeros and add to the total
|
||||
$Octets = $IP -split "\."
|
||||
$IPInBinary = @()
|
||||
foreach ($Octet in $Octets) {
|
||||
#convert to binary
|
||||
$OctetInBinary = [convert]::ToString($Octet, 2)
|
||||
|
||||
#get length of binary string add leading zeros to make octet
|
||||
$OctetInBinary = ("0" * (8 - ($OctetInBinary).Length) + $OctetInBinary)
|
||||
|
||||
$IPInBinary = $IPInBinary + $OctetInBinary
|
||||
}
|
||||
$IPInBinary = $IPInBinary -join ""
|
||||
|
||||
#Get network ID by subtracting subnet mask
|
||||
$HostBits = 32 - $SubnetBits
|
||||
$NetworkIDInBinary = $IPInBinary.Substring(0, $SubnetBits)
|
||||
|
||||
#Get host ID and get the first host ID by converting all 1s into 0s
|
||||
$HostIDInBinary = $IPInBinary.Substring($SubnetBits, $HostBits)
|
||||
$HostIDInBinary = $HostIDInBinary -replace "1", "0"
|
||||
|
||||
#Work out all the host IDs in that subnet by cycling through $i from 1 up to max $HostIDInBinary (i.e. 1s stringed up to $HostBits)
|
||||
#Work out max $HostIDInBinary
|
||||
$imax = [convert]::ToInt32(("1" * $HostBits), 2) - 1
|
||||
|
||||
$IPs = @()
|
||||
|
||||
#Next ID is first network ID converted to decimal plus $i then converted to binary
|
||||
For ($i = 1 ; $i -le $imax ; $i++) {
|
||||
#Convert to decimal and add $i
|
||||
$NextHostIDInDecimal = ([convert]::ToInt32($HostIDInBinary, 2) + $i)
|
||||
#Convert back to binary
|
||||
$NextHostIDInBinary = [convert]::ToString($NextHostIDInDecimal, 2)
|
||||
#Add leading zeros
|
||||
#Number of zeros to add
|
||||
$NoOfZerosToAdd = $HostIDInBinary.Length - $NextHostIDInBinary.Length
|
||||
$NextHostIDInBinary = ("0" * $NoOfZerosToAdd) + $NextHostIDInBinary
|
||||
|
||||
#Work out next IP
|
||||
#Add networkID to hostID
|
||||
$NextIPInBinary = $NetworkIDInBinary + $NextHostIDInBinary
|
||||
#Split into octets and separate by . then join
|
||||
$IP = @()
|
||||
For ($x = 1 ; $x -le 4 ; $x++) {
|
||||
#Work out start character position
|
||||
$StartCharNumber = ($x - 1) * 8
|
||||
#Get octet in binary
|
||||
$IPOctetInBinary = $NextIPInBinary.Substring($StartCharNumber, 8)
|
||||
#Convert octet into decimal
|
||||
$IPOctetInDecimal = [convert]::ToInt32($IPOctetInBinary, 2)
|
||||
#Add octet to IP
|
||||
$IP += $IPOctetInDecimal
|
||||
}
|
||||
|
||||
#Separate by .
|
||||
$IP = $IP -join "."
|
||||
$IPs += $IP
|
||||
|
||||
|
||||
}
|
||||
$IPs
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Dynamically creates DNS A records
|
||||
.DESCRIPTION
|
||||
Dynamically creates DNS A records based on CIDR input
|
||||
.EXAMPLE
|
||||
New-ITDIbDNSRecordNextAvailableIP -Hostname itdserver1.nd.gov -CIDR 10.11.12.0/23 -Credential $Credential
|
||||
.EXAMPLE
|
||||
New-ITDIbDNSRecordNextAvailableIP -Hostname itdserver2.nd.gov,itdserver3.nd.gov,itdserver4.nd.gov -CIDR 10.11.12.0/23 -Credential $Credential
|
||||
.INPUTS
|
||||
Inputs to this cmdlet (if any)
|
||||
.OUTPUTS
|
||||
Output from this cmdlet (if any)
|
||||
.NOTES
|
||||
General notes
|
||||
.COMPONENT
|
||||
The component this cmdlet belongs to
|
||||
.ROLE
|
||||
The role this cmdlet belongs to
|
||||
.FUNCTIONALITY
|
||||
The functionality that best describes this cmdlet
|
||||
#>
|
||||
function New-ITDIbDNSRecordNextAvailableIP {
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNull()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string[]]
|
||||
$Hostname,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNull()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidatePattern("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(3[0-2]|[1-2][0-9]|[0-9]))$")]
|
||||
[string]
|
||||
$CIDR,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
begin {
|
||||
$NetworkAddress = $CIDR.split('/')[0]
|
||||
$IPs = Get-IPs -Subnets $CIDR
|
||||
$ExcludeIPs = $IPs | select -First 1
|
||||
}
|
||||
|
||||
process {
|
||||
#$ curl -k -u admin:infoblox -X GET https://10.64.41.6/wapi/v1.1/network?ipv4addr=10.144.2.0
|
||||
try {
|
||||
$networkobj = Invoke-RestMethod -Method Get -Uri "https://infoblox-gmv.ns.nd.gov/wapi/v2.7/network?ipv4addr=$NetworkAddress" -Credential $Credential -ContentType "application/json" -ErrorAction Stop
|
||||
$networkobjref = $networkobj._ref
|
||||
|
||||
#curl -k -u admin:infoblox -X POST https://10.64.41.6/wapi/v1.1/network/ZG5zLm5ldHdvcmskMTAuMTQ0LjIuMC8yNC8w:10.144.2.0/24/default?_function=next_available_ip -H "Content-Type: application/json" -d '{"exclude": ["10.144.2.8", "10.144.2.10"], "num": 6}'
|
||||
$bodyjson = @{num = @($Hostname).count; exclude = @($ExcludeIPs) } | ConvertTo-Json
|
||||
#Invoke-RestMethod -Method Post -Uri ("https://infoblox-gmv.ns.nd.gov/wapi/v2.7/" + $networkobjref + "?_function=next_available_ip") -ContentType "application/json" -Credential $Credential -Body '{"exclude": ["10.11.12.1", "10.11.12.2"], "num": 6}' #WORKS
|
||||
$IPsAvailable = (Invoke-RestMethod -Method Post -Uri ("https://infoblox-gmv.ns.nd.gov/wapi/v2.7/" + $networkobjref + "?_function=next_available_ip") -ContentType "application/json" -Credential $Credential -Body $bodyjson -ErrorAction Stop).Ips
|
||||
|
||||
$IPCount = 0
|
||||
|
||||
ForEach ($hn in $hostname) {
|
||||
if (Get-ITDIbDNSRecord -Hostname $hn -Credential $Credential) {
|
||||
Write-Warning "DNS record already exists"
|
||||
}
|
||||
else {
|
||||
New-ITDIbDNSRecord -IPv4Address $IPsAvailable[$IPCount] -Hostname $hn -Credential $Credential -ErrorAction Stop
|
||||
$IPcount++
|
||||
}
|
||||
}
|
||||
}
|
||||
catch [System.Net.WebException] {
|
||||
Write-Error "webexception error"
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ITDIbVlan {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(
|
||||
ParameterSetName = 'VlanLookup')]
|
||||
[ValidateRange(0, 4096)]
|
||||
[int[]]
|
||||
$Vlan,
|
||||
|
||||
[Parameter(
|
||||
ParameterSetName = 'CIDRLookup')]
|
||||
[string[]]
|
||||
$CIDR,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
begin {
|
||||
|
||||
}
|
||||
process {
|
||||
$Output = @()
|
||||
|
||||
switch ($PsCmdlet.ParameterSetName) {
|
||||
"VlanLookup" {
|
||||
ForEach ($v in $Vlan) {
|
||||
$InvokeResult = Invoke-RestMethod -Method Get -Uri "https://infoblox-gmv.ns.nd.gov/wapi/v2.11.3/vlan?id=$v&_return_as_object=1&_return_fields=assigned_to,id,name,parent,comment,description" -ContentType "application/json" -Credential $Credential
|
||||
$InvokePSObject = $InvokeResult.result | Select-Object id, Name, Comment, @{n = "AssignedTo"; e = { $_.Assigned_to.split(':')[1] -replace '/default' } }
|
||||
$Output += $InvokePSObject
|
||||
}
|
||||
}
|
||||
"CIDRLookup" {
|
||||
ForEach ($c in $CIDR) {
|
||||
$InvokeResult = Invoke-RestMethod -Method Get -Uri "https://infoblox-gmv.ns.nd.gov/wapi/v2.11.3/vlan?_return_as_object=1&_max_results=-50000&_return_fields=assigned_to,id,name,parent,comment,description" -ContentType "application/json" -Credential $Credential
|
||||
$InvokePSObject = $InvokeResult.result | Select-Object id, Name, Comment, @{n = "AssignedTo"; e = { $_.Assigned_to.split(':')[1] -replace '/default' } }
|
||||
$Output += $InvokePSObject | Where-Object AssignedTo -EQ $c
|
||||
}
|
||||
}
|
||||
"default" {
|
||||
$InvokeResult = Invoke-RestMethod -Method Get -Uri "https://infoblox-gmv.ns.nd.gov/wapi/v2.11.3/vlan?_return_as_object=1&_max_results=-50000&_return_fields=assigned_to,id,name,parent,comment,description" -ContentType "application/json" -Credential $Credential
|
||||
$InvokePSObject = $InvokeResult.result | Select-Object id, Name, Comment, @{n = "AssignedTo"; e = { $_.Assigned_to.split(':')[1] -replace '/default' } }
|
||||
$Output = $InvokePSObject
|
||||
}
|
||||
}
|
||||
}
|
||||
end {
|
||||
Write-Output $Output
|
||||
}
|
||||
}
|
||||
|
||||
function Remove-ITDIbDnsRecord {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$ComputerName,
|
||||
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
|
||||
Begin {
|
||||
If (!($Credential)) {
|
||||
$Credential = Get-Credential
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Process {
|
||||
$DNSRecord = Invoke-RestMethod -Method Get -Uri "https://infoblox-gmv.ns.nd.gov/wapi/v2.7/record:host?name=$ComputerName" -ContentType "application/json" -Credential $Credential
|
||||
$UriToDelete = ( "https://infoblox-gmv.ns.nd.gov/wapi/v2.7/" + $DNSRecord._ref )
|
||||
Invoke-RestMethod -Method Delete -Uri $UriToDelete -ContentType "application/json" -Credential $Credential
|
||||
}
|
||||
|
||||
end {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Introduction
|
||||
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
|
||||
|
||||
# Getting Started
|
||||
TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
|
||||
1. Installation process
|
||||
2. Software dependencies
|
||||
3. Latest releases
|
||||
4. API references
|
||||
|
||||
# Build and Test
|
||||
TODO: Describe and show how to build your code and run the tests.
|
||||
|
||||
# Contribute
|
||||
TODO: Explain how other users and developers can contribute to make your code better.
|
||||
|
||||
If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
|
||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
||||
@@ -0,0 +1,49 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
name: 'ITD.Infra-Passwordstate'
|
||||
|
||||
variables:
|
||||
major: 0
|
||||
minor: 2
|
||||
patch: $(Build.BuildID)
|
||||
buildVer: $(major).$(minor).$(Build.BuildID)
|
||||
|
||||
pool: itdwinautop1
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Build
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
filePath: '$(System.DefaultWorkingDirectory)/Build/build.ps1'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: '$(System.DefaultWorkingDirectory)/ITD.Infra-Passwordstate.nuspec'
|
||||
versioningScheme: byEnvVar
|
||||
versionEnvVar: buildVer
|
||||
buildProperties: 'VERSIONHERE=$(buildVer)'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'NuGetPackage'
|
||||
publishLocation: 'Container'
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Deploy
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'NuGetPackage'
|
||||
itemPattern: '**'
|
||||
targetPath: '$(Pipeline.Workspace)'
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Pipeline.Workspace)/ITD.Infra-Passwordstate.$(major).$(minor).$(Build.BuildID).nupkg'
|
||||
nuGetFeedType: external
|
||||
publishFeedCredentials: 'ITD_PwshGallery'
|
||||
@@ -0,0 +1,17 @@
|
||||
$buildVersion = $env:BUILDVER
|
||||
$moduleName = 'ITD.Infra-Passwordstate'
|
||||
|
||||
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"
|
||||
$modulePath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psm1"
|
||||
|
||||
## Update build version in manifest
|
||||
$manifestContent = Get-Content -Path $manifestPath -Raw
|
||||
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
|
||||
|
||||
## Update functions to export in manifest
|
||||
Import-Module $modulePath
|
||||
$funcStrings = (Get-Module ITD.Infra-Passwordstate).ExportedCommands.Values.Name
|
||||
$funcStrings = "'$($funcStrings -join "','")'"
|
||||
$manifestContent = $manifestContent -replace "<FunctionsToExport>", $funcStrings
|
||||
|
||||
$manifestContent | Set-Content -Path $manifestPath
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>ITD.Infra-Passwordstate</id>
|
||||
<version>$VERSIONHERE$</version>
|
||||
<authors>Zack Meier</authors>
|
||||
<description>Functions for Passwordstate use</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="**\.git\**;**\Build\**" />
|
||||
</files>
|
||||
</package>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user