Busque y reemplace la cadena de la base de datos en todas las web.configs a través de una pila -- powershell camp codereview Relacionados El problema

Find and replace database string in all Web.configs across a stack


4
vote

problema

Español

Me encargué esta tarea con casi ningún tiempo y no puedo soportar un nuevo entorno para probar esto. El riesgo es bastante alto con lo que es la solicitud, por lo que estoy solicitando la revisión de los compañeros y los códigos que pueda obtener.

Estoy encargado de revisar todas las web.configs que tienen un cierto servidor de DB y cambiarlo a un nuevo valor.

Esto sucederá en aproximadamente 250 servidores con una ventana de mantenimiento de aproximadamente una hora.

Mi primer pase Quiero que encuentre las configuraciones y colóquelo en una carpeta en mi máquina local para que revise el cambio con C: NewConfigs Fullpathofconfighere

Segunda Pass En realidad, estableceré contenido o crearé una nueva configuración que (esto se comenta por ahora) a las configuraciones en su ubicación actual. Todo lo cual está en una D: o E: Drive.

  default(Day)3  
Original en ingles

I was tasked with this assignment with almost no time and I cannot stand up a new environment to test this. The risk is pretty high with what is the request is for so I'm asking for as much peer and code review as I can get.

I'm tasked to check all the web.configs that have a certain DB Server and change it to a new value.

This will happen across about 250 servers with about an hour maintenance window.

My first pass I want it to find the configs and place it in a folder on my local machine for me to review the change with C:\NewConfigs\FullPathofConfigHere

Second pass I will actually set-content or create a new config which (this is commented out for now) to the configs in their current location. All of which is on either a D: or E: Drive.

$servers = Get-Content "servers.txt" $WebConfigFile = "web.config" $connectionstring1 = "DBstring1.local.domain" $connectionstring2 = "DBstring2.local.domain" $to = "C:\ConfigFinder\BackupConfigs\" $NewFolder = "C:\ConfigFinder\NewConfigs\"  Function Backup {     foreach ($computer in $servers) {          Get-ChildItem -Recurse -Force \\$computer\d$ -ErrorAction SilentlyContinue -Include $WebConfigFile |          Where-Object {$_.FullName -notlike "*Recycle.bin*"} |         Select-Object FullName |         #This makes a backup copy before doing any rewrite         % {             $newpath = join-path $To $_.FullName.ToLower()             md $newpath             Copy-Item $_.FullName.ToLower() -destination $newpath -verbose         }          Get-ChildItem -Recurse -Force \\$computer\e$ -ErrorAction SilentlyContinue -Include $WebConfigFile |          Where-Object {$_.FullName -notlike "*Recycle.bin*"} |         Select-Object FullName |         #Select-String $connectionstring1 |         #This makes a backup copy before doing any rewrite                      % {             $newpath = join-path $To $_.FullName             md $newpath             Copy-Item $_.FullName.ToLower() -destination $newpath -verbose         }      } } Function CreateLocal {     foreach ($computer in $servers) {          Get-ChildItem -Recurse -Force \\$computer\d$ -ErrorAction SilentlyContinue -Include $WebConfigFile |          Where-Object {$_.FullName -notlike "*Recycle.bin*"} |         Select-Object FullName |         #Select-String $connectionstring1 |          #This makes a backup copy before doing any rewrite         % {             $ConfigName = "Web.qa.Config"             $newpath = join-path $NewFolder $_.FullName.Replace("Web.config","")             md $newpath             $finaldestination = $newpath + $ConfigName             (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Out-File $finaldestination         }         Get-ChildItem -Recurse -Force \\$computer\e$ -ErrorAction SilentlyContinue -Include $WebConfigFile |          Where-Object {$_.FullName -notlike "*Recycle.bin*"} |         Select-Object FullName |         #This makes a backup copy before doing any rewrite                      % {             $ConfigName = "Web.qa.Config"             $newpath = join-path $NewFolder $_.FullName.Replace("Web.config","")             md $newpath             $finaldestination = $newpath + $ConfigName             (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Out-File $finaldestination         }      } } Function ConfigonServer {     Write-Host "CAUTION YOU ARE ABOUT TO WRITE NEW CONFIGS ON THE SERVERS"     $resp = Read-Host " Are you SURE you want to continue? (Y/[N])"     if ($Resp.ToUpper() -eq "N") {         Write-Host "Taking you back to Safety"         sleep 3         Menu      }     if ($Resp.ToUpper() -eq "Y") {         foreach ($computer in $servers) {              Get-ChildItem -Recurse -Force \\$computer\d$ -ErrorAction SilentlyContinue -Include $WebConfigFile |              Where-Object {$_.FullName -notlike "*Recycle.bin*"}  |             Select-Object FullName |             #Select-String $connectionstring1 |              #This makes a backup copy before doing any rewrite             % {                 $ConfigName = "Web.qa.Config"                 $finaldestination = $_.FullName.replace("Web.config","") + $ConfigName                 (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Out-File $finaldestination -encoding "UTF8"             }             Get-ChildItem -Recurse -Force \\$computer\e$ -ErrorAction SilentlyContinue -Include $WebConfigFile |              Where-Object {$_.FullName -notlike "*Recycle.bin*"} |             Select-Object FullName |             #Select-String $connectionstring1 |             #This makes a backup copy before doing any rewrite                          % {                 $ConfigName = "Web.qa.Config"                 $finaldestination = $_.FullName.replace("Web.config","") + $ConfigName                 (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Out-File $finaldestination -encoding "UTF8"             }         }      } } Function HoldMyBeer {     Write-Host "CAUTION YOU ARE ABOUT TO RE-WRITE ALL THE CONFIGS"     $resp = Read-Host " Are you SURE you want to continue? (Y/[N])"     if ($Resp.ToUpper() -eq "N") {         Write-Host "Taking you back to Safety"         sleep 3         Menu      }     if ($Resp.ToUpper() -eq "Y") {          foreach ($computer in $servers) {              Get-ChildItem -Recurse -Force \\$computer\d$ -ErrorAction SilentlyContinue -Include $WebConfigFile |              Where-Object {$_.FullName -notlike "*Recycle.bin*"} |             Select-Object FullName |             #Select-String $connectionstring1 |              #This makes a backup copy before doing any rewrite             % {                 (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Set-Content $_.FullName -encoding "UTF8"             }             Get-ChildItem -Recurse -Force \\$computer\e$ -ErrorAction SilentlyContinue -Include $WebConfigFile |              Where-Object {$_.FullName -notlike "*Recycle.bin*"} |             Select-Object FullName |             #Select-String $connectionstring1 |             #This makes a backup copy before doing any rewrite                       % {                 (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Set-Content $_.FullName -encoding "UTF8"             }         }     } } Function Menu      {     Do {         Write-Host ""         Write-Host ""         Write-Host     "===================================================="         Write-Host "What would you like to do Today?"         Write-Host     "===================================================="         Write-Host ""         Write-Host "1. Backup to Local Disk" -foregroundcolor green         Write-Host "2. Create New Strings to Local Disk" -foregroundcolor cyan         Write-Host "3. Create New Configs on The Server List" -foregroundcolor yellow         Write-Host "4. Re-write the Files on the servers)" -foregroundcolor magenta         Write-Host "5. Exit"          Write-Host ""         Write-Host ""         Write-Host $errout         $Choice = Read-Host '(1-5)'          switch ($Choice) {             1 {                 Backup; break             }             2 {                 CreateLocal; break             }             3 {                 ConfigonServer; break             }             4 {                 HoldMyBeer; break             }             5 {                 Exit;exit             }             default {                 $errout = "No, try again........Try 1-5 only"             }          }      }     until ($Choice -ne "") } Menu 
  
         
         

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

Sensibilidad del caso del operador

¿De manera predeterminada (todos?) De los operadores de comparación, como def __init__(self, edges=()): """Construct a graph containing the edges from the given iterable. An edge between vertices v1 and v2 with weight w should be specified as a tuple (v1, v2, w). """ self._g = defaultdict(dict) for edge in edges: self.add(*edge) def add(self, v1, v2, w): """Add an edge between vertices v1 and v2 with weight w. If an edge already exists between these vertices, set its weight to w. """ self._g[v1][v2] = self._g[v2][v1] = w 33 y def __init__(self, edges=()): """Construct a graph containing the edges from the given iterable. An edge between vertices v1 and v2 with weight w should be specified as a tuple (v1, v2, w). """ self._g = defaultdict(dict) for edge in edges: self.add(*edge) def add(self, v1, v2, w): """Add an edge between vertices v1 and v2 with weight w. If an edge already exists between these vertices, set its weight to w. """ self._g[v1][v2] = self._g[v2][v1] = w 34 , son caso- insensible de forma predeterminada. Lo que eso significa es ese código como

  def __init__(self, edges=()):     """Construct a graph containing the edges from the given iterable.     An edge between vertices v1 and v2 with weight w should be     specified as a tuple (v1, v2, w).      """     self._g = defaultdict(dict)     for edge in edges:         self.add(*edge)  def add(self, v1, v2, w):     """Add an edge between vertices v1 and v2 with weight w.     If an edge already exists between these vertices, set its     weight to w.      """     self._g[v1][v2] = self._g[v2][v1] = w 5  

es redundante y funcionalmente igual a

  def __init__(self, edges=()):     """Construct a graph containing the edges from the given iterable.     An edge between vertices v1 and v2 with weight w should be     specified as a tuple (v1, v2, w).      """     self._g = defaultdict(dict)     for edge in edges:         self.add(*edge)  def add(self, v1, v2, w):     """Add an edge between vertices v1 and v2 with weight w.     If an edge already exists between these vertices, set its     weight to w.      """     self._g[v1][v2] = self._g[v2][v1] = w 6  

Mientras que en el tema de la sensibilidad de la caja def __init__(self, edges=()): """Construct a graph containing the edges from the given iterable. An edge between vertices v1 and v2 with weight w should be specified as a tuple (v1, v2, w). """ self._g = defaultdict(dict) for edge in edges: self.add(*edge) def add(self, v1, v2, w): """Add an edge between vertices v1 and v2 with weight w. If an edge already exists between these vertices, set its weight to w. """ self._g[v1][v2] = self._g[v2][v1] = w 7 no tiene ningún propósito aquí

  def __init__(self, edges=()):     """Construct a graph containing the edges from the given iterable.     An edge between vertices v1 and v2 with weight w should be     specified as a tuple (v1, v2, w).      """     self._g = defaultdict(dict)     for edge in edges:         self.add(*edge)  def add(self, v1, v2, w):     """Add an edge between vertices v1 and v2 with weight w.     If an edge already exists between these vertices, set its     weight to w.      """     self._g[v1][v2] = self._g[v2][v1] = w 8  

Convención de nombres de verbo-noun

Las recomendaciones de nombración de funciones de PowerShell son verbizas. La acción que está realizando y el objeto de su acción. Usted ve que todo esto es cmdlets de stock como def __init__(self, edges=()): """Construct a graph containing the edges from the given iterable. An edge between vertices v1 and v2 with weight w should be specified as a tuple (v1, v2, w). """ self._g = defaultdict(dict) for edge in edges: self.add(*edge) def add(self, v1, v2, w): """Add an edge between vertices v1 and v2 with weight w. If an edge already exists between these vertices, set its weight to w. """ self._g[v1][v2] = self._g[v2][v1] = w 9 . MSDN tiene una lista extensa pero sencilla de seguir la lista de recomendaciones . Intentando y alinee el nombre lo mejor posible con lo que está haciendo el cmdlet / Función / código. Si tiene un problema, averiguar un nombre, es posible que necesite romper ese código en piezas separadas.

No criticar demasiado has_link0 . Al menos has_link1 sería mejor :)

El sistema de menú de elección

PowerShell tiene una excelente manera de crear menús que guían la entrada de usuarios y actuando sobre el resultados . No es demasiado difícil obtener una comprensión a primera vista, por lo que voy a incluir el fragmento de código de la parte delantera de ese artículo.

  has_link2  

Esto tendría más funcionalidad y menos preocupante por las selecciones de código y enduser. De esta manera, usted sabe que el endUser solo puede seleccionar una opción válida.

Seleccionar objeto -expandproperty

Cuando está usando has_link3 Está obteniendo una matriz de objetos que contiene las propiedades que solicitó. Este es el caso incluso si selecciona una propiedad.

Para realizar el uso posterior de esa propiedad única, usted está devuelve esa propiedad como una matriz como se supone en una matriz de objetos con esa propiedad. Considere el siguiente código:

  has_link4  

Esto podría ser simplificado

  has_link5  

Esto también verá un efecto positivo en otras partes de su código a medida que se refiere a has_link6 con frecuencia.

Repetición del código

Si se encuentra repitiendo el mismo código una y otra vez, debe preguntarse si hay otra forma. Las funciones y el mejor uso de los parámetros del cmdlet aquí harían algún avance para usted.

Está recopilando archivos con bastante frecuencia. Aunque el código se usa solo una vez por selección de menú. Sin embargo, si tuviera que hacer un cambio lógico, tendría que asegurarlo de hacerlo en alrededor de 8 lugares. Ese es un gran margen por error.

Diablos, incluso podría consolidar estos dos bloques, ya que GET-HIGHNITEM es compatible con las matrices para has_link7

  has_link8  

La primera línea funcionaría con este pequeño cambio.

  has_link9  

Eso reduce el segundo bloque por completo ya que ambos parecen hacer lo mismo.

Filtro & GT; Incluir

Dado que solo está tratando de encontrar archivos con un nombre determinado y está buscando recursivamente a través de la unidad, encontrará que 998877766555443350 (código> se realizará __getitem__1 desde que funciona en el proveedor nivel. Desde Get-ChildItem en MSDN

Los filtros son más eficientes que otros parámetros, porque el proveedor los aplica al recuperar los objetos, en lugar de tener un filtro de PowerShell de Windows, los objetos después de que se recuperen del proveedor.

Consistencia

Veo que está utilizando ambos __getitem__2 y __getitem__3 . Elige uno y quédate con él. Si alguien más está leyendo su código, es posible que tengan que pasar el tiempo preguntándose por qué eligió esto. __getitem__4 debe funcionar mejor entre los dos.

Trepidación de ejecutar este código

Si está en todo lo relacionado con la ejecución de este código, sería un ejercicio trivial configurar un entorno de prueba para asegurarse de que su código solo haga lo que espera.


Hay algunas otras áreas que se aborden , pero las anteriores serían las que actuaría enfoque lleno primero.

 

Operator Case Sensitivity

By default most (all?) of the comparison operators, like -eq and -like, are case-insensitive by default. What that means is that code like

if ($Resp.ToUpper() -eq "N") 

is redundant and functionally the same as

if ($Resp -eq "N") 

While on the topic of case sensitivity .ToLower() serves no purpose here

Copy-Item $_.FullName.ToLower() 

Verb-Noun Naming Convention

PowerShell function naming recommendations are Verb-Noun(s). The action you are performing and the object of your action. You see this is all stock cmdlets like Get-Item. MSDN has an extensive but simple to follow list of recommendations. Trying and align the name as best as possible with what the cmdlet/function/code is doing. If you are having an issue figuring out a name it is possible that you need to break up that code into separate pieces.

Not to overly criticize HoldMyBeer. At least Hold-Beer would be better :)

The Choice Menu System

PowerShell has a great way of creating menus guiding user input and acting on the results. It is not too hard to get a grasp at first glance so I am going to include the code snippet from the from the front of that article.

$title = "Delete Files" $message = "Do you want to delete the remaining files in the folder?"  $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` "Deletes all the files in the folder."  $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", ` "Retains all the files in the folder."  $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)  $result = $host.ui.PromptForChoice($title, $message, $options, 0)  switch ($result) { 0 {"You selected Yes."} 1 {"You selected No."} } 

This would have more functionality and less worrying about code and enduser selections. This way you know the enduser can only select a valid option.

Select-Object -ExpandProperty

When you are using select-object you are getting an object array containing the properties you requested. This is the case even if you select one property.

To make subsequent use of that single property you are return just that property as an array as supposed to an object array with that property. Consider the following code:

Get-ChildItem -Recurse -Force \\$computer\e$ -ErrorAction SilentlyContinue -Include $WebConfigFile |        Where-Object {$_.FullName -notlike "*Recycle.bin*"} |       Select-Object FullName | # .... 

This could be simplified

Get-ChildItem -Recurse -Force "\\$computer\e$" -ErrorAction SilentlyContinue -Include $WebConfigFile |      Select-Object -ExpandProperty FullName |      Where-Object {$_ -notlike "*Recycle.bin*"} | # .... 

This will also see a positive effect in other parts of your code as you refer to fullname frequently.

Code Repetition

If you find yourself repeating the same code over and over again you should be asking if there is another way. Functions and better use of cmdlet parameters here would make some headway for you.

You are gathering files rather frequently. Albeit the code is used only once per menu selection. However if you had to make a logic change you would have to ensure your doing it in around 8 places. That is a huge margin for error.

Heck you could even consolidate both these blocks since Get-ChildItem supports arrays for -Path

    Get-ChildItem -Recurse -Force \\$computer\d$ -ErrorAction SilentlyContinue -Include $WebConfigFile |      Where-Object {$_.FullName -notlike "*Recycle.bin*"}  |     Select-Object FullName |     #Select-String $connectionstring1 |      #This makes a backup copy before doing any rewrite     % {         $ConfigName = "Web.qa.Config"         $finaldestination = $_.FullName.replace("Web.config","") + $ConfigName         (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Out-File $finaldestination -encoding "UTF8"     }     Get-ChildItem -Recurse -Force \\$computer\e$ -ErrorAction SilentlyContinue -Include $WebConfigFile |      Where-Object {$_.FullName -notlike "*Recycle.bin*"} |     Select-Object FullName |     #Select-String $connectionstring1 |     #This makes a backup copy before doing any rewrite                  % {         $ConfigName = "Web.qa.Config"         $finaldestination = $_.FullName.replace("Web.config","") + $ConfigName         (Get-Content $_.FullName).replace($connectionstring1, $connectionstring2) | Out-File $finaldestination -encoding "UTF8"     } 

The first line would work with this small change.

Get-ChildItem -Recurse -Force "\\$computer\d$", "\\$computer\e$" -ErrorAction SilentlyContinue 

That cuts out the second block entirely since they both appear to do the same thing.

Filter > Include

Since you are only trying to find files with a certain name and are searching recursively through drive you will find that -Filter will out perform -Include since it functions at the provider level. From Get-ChildItem on MSDN

Filters are more efficient than other parameters, because the provider applies them when retrieving the objects, rather than having Windows PowerShell filter the objects after they are retrieved from the provider.

Consistency

I see that you are using both Out-File and Set-Content. Pick one and stick with it. If someone else is reading your code they may have to spend time wondering why you chose this. Set-Content should perform better between the two.

Trepidation of running this code

If you are at all concerned about running this code it would be a trivial exercise to set up a test environment to ensure that your code only does what you expect it to.


There are some other areas that could be addressed but the ones above would be the ones I would actually focus on first.

 
 
   
   

Relacionados problema

1  Exportando bolsas, listas de saltos y archivos LNK con PowerShell  ( Exporting shellbags jump lists and lnk files with powershell ) 
Soy nuevo en PowerShell (comenzó ayer) y se preguntó si hay una forma de exportar bolsas de cáscara, listas de saltos y archivos LNK de la Región de Región de...

9  Script PowerShell para manipular archivos de Excel  ( Powershell script for manipulating excel files ) 
Tengo un directorio de libros de trabajo .xls con la siguiente convención de nomenclatura: public class Hand { // in general it is one idea more corr...

2  Resume un directorio con PowerShell  ( Summarize a directory with powershell ) 
Estoy tratando de generar información agregada sobre un árbol de directorios (extensión de archivo más conteo y tamaño acumulativo por tipo de archivo) con Po...

8  Actualizando múltiples repositorios SVN usando PowerShell  ( Updating multiple svn repositories using powershell ) 
Escribí un script para realizar ReportWithSections2 en todos los repositorios SVN dentro de una carpeta. Hubiera sido genial si la carpeta que contiene todo...

10  Comparación de hash MD5 para dos carpetas  ( Md5 hash comparison for two folders ) 
usando esta página para un punto de partida para comparar el hash md5 Valores de los archivos en dos carpetas diferentes, he colocado algo juntos que las em...

9  Convierta una contraseña a una cadena fonética para usuarios finales  ( Convert a password to a phonetic string for end users ) 
Tanto como lo odio, a veces proporcionar contraseñas a las personas debe hacerse electrónicamente. Cuando hago eso, trato de eliminar cualquier ambigüedad que...

6  Sherlock va en contra de la bestia con PowerShell  ( Sherlock is going against the beast with powershell ) 
I corrí a este desafío un par de veces hoy y pensé que podría abordarlo con Potencia Shell. Me negué a mirar a otros enfoques de los pueblos a esto con la e...

3  Eliminar seda huérfana + cambio a control completo  ( Delete orphaned sids change to full control ) 
Necesito eliminar todos los SID huérfanos en las ACL de aproximadamente 20 acciones (entre 100 GB / 6TB) y cambiar el control total de los grupos de usuarios ...

6  Extraiga la cadena de la versión del archivo de texto con PowerShell  ( Extract version string from text file with powershell ) 
Necesito tirar de la versión # de un script de VIM que podría parecer algo así: #!/bin/bash LAST=`exec ls example_dir | sed 's/([0-9]+).*/1/g' | sort -n |...

2  Agregando algunas propiedades a un usuario de Active Directory  ( Adding some properties to an active directory user ) 
Estaba escribiendo la siguiente sección del código que agrega algunas propiedades de usuario a un usuario de Active Directory, y se preguntó, seguramente pode...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos