Select to view content in your preferred language

Review AWS ALB Cloudformation template/discussion

496
1
Jump to solution
08-07-2024 04:53 PM
Labels (1)
KarlC
by
New Contributor

We are in the process of deploying ArcGIS Enterprise 11.1 and I have create a Cloudformation stack to deploy all the infra to AWS, however I am not overly familiar with the configuration of ArcGIS Enterprise, I've read a lot of documentation but would appreciate if someone more knowledgeable could review.

Our Planned installation will have a publicly accessible load balancer running under gis.company.com and we have Portal, ArcGIS Server, Datastore, a geoprocessing server, a imagery processing server, Ideally we would use SAML or OpenID to Azure AD/Entra ID and the arcgis and processing servers would be federated to portal (is this right?). We are planning to use FSX for configuration storage and I think I have set this up right. Also planning not to use the webadaptor

I was planning on routing paths /arcgis/rest/*  /arcgis/services/*, /arcgis/admin to a target group for ArcGIS Servers, paths /portal/*, /arcgis/home/* /arcgis/sharing/*, /arcgis/portaladmin/* and /arcgis/portaladmin?* to a target group for Portal and /image/* to target group for Image Processing and /geoprocessing/* to a targetgroup for geoprocessing however I am not sure if this will work.

When using AWS Application Load balancer should I be directing 443 to 6443 on ArcGIS server or to 6080? Do I need to get certificate and install it on ArcGIS Server?

 

 

{
    "Description": "Application Load balancer template or ESRI Setup",
    "Parameters":{
        "StackName":{
            "Type":"String"
        },
        "BucketName":{
            "Type":"String"
        },
        "VPCId":{
            "Type":"String"
        },
        "PublicSubnets":{
            "Type":"CommaDelimitedList"
        },
        "PrivateSubnets":{
            "Type":"CommaDelimitedList"
        },
        "PortalInternalPort":{
            "Type": "Number",
            "Default": 7443
        },
        "PortalExternalPort":{
            "Type": "Number",
            "Default": 443
        },
        "AGSInternalPort":{
            "Type": "Number",
            "Default": 6443
        },
        "AGSExternalPort":{
            "Type": "Number",
            "Default": 443
        },
        "AGSFederationExternalPort":{
            "Type": "Number",
            "Default": 6443
        },
        "PortalFederationExternalPort":{
            "Type": "Number",
            "Default": 7443
        },
        "CertificateArn":{
            "Type": "String"
        },
        "ALBSGId":{
            "Type": "String"
        },
        "HealthCheckIntervalSeconds":{
            "Type": "Number"
        },
        "PortalHealthCheckPath":{
            "Type": "String"
        },
        "AGSHealthCheckPath":{
            "Type": "String"
        },
        "HealthCheckProtocol":{
            "Type": "String"
        },
        "PortalInstanceId":{
            "Type": "String"
        },
        "HealthCheckTimeoutSeconds":{
            "Type": "Number",
            "Default": 10
        },
        "AGSInstanceId":{
            "Type": "String"
        },
        "ImageProcInstanceId":{
            "Type": "String"
        },
        "GeoProcInstanceId":{
            "Type": "String"
        },
        "AccessLogPrefix":{
            "Type": "String",
            "Default": "access_log"
        },
        "ConnectionLogPrefix":{
            "Type": "String",
            "Default": "connection_log"
        }
    },
    "Resources":{
        "ALBLogBucket":{
            "Type":"AWS::S3::Bucket",
            "Properties":{
                "BucketName":{"Ref":"BucketName"}
            }
        },
        "ALBLogBucketPolicy":{
            "Type":"AWS::S3::BucketPolicy",
            "Properties":{
                "Bucket":{"Ref":"BucketName"},
                "PolicyDocument":{
                    "Version": "2012-10-17",
                    "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": "arn:aws:iam::783225319266:root"
                        },
                        "Action": "s3:PutObject",
                        "Resource": [
                            {"Fn::Sub":"arn:aws:s3:::${BucketName}/${AccessLogPrefix}/AWSLogs/${AWS::AccountId}/*"},
                            {"Fn::Sub":"arn:aws:s3:::${BucketName}/${ConnectionLogPrefix}/AWSLogs/${AWS::AccountId}/*"}
                        ]
                    }
                    ]
                }
            }
        },
        "ALB": {
            "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
            "Properties": {
                "LoadBalancerName": {"Fn::Sub": "${AWS::StackName}-ALB"},
                "Subnets": [
                    {"Fn::Select": [0,{"Ref": "PublicSubnets"}]},
                    {"Fn::Select": [1,{"Ref": "PublicSubnets"}]}
                ],
                "Type": "application",
                "SecurityGroups": [{"Ref": "ALBSGId"}],
                "Scheme": "internet-facing",
                "LoadBalancerAttributes": [
                    {
                        "Key": "idle_timeout.timeout_seconds",
                        "Value": "600"
                    },
                    
                    {
                        "Key": "access_logs.s3.enabled",
                        "Value": true
                    },
                    {
                        "Key": "access_logs.s3.bucket",
                        "Value": { "Ref": "ALBLogBucket"}
                    },
                    {
                        "Key":"access_logs.s3.prefix",
                        "Value": {"Ref":"AccessLogPrefix"}
                    },
                    {
                        "Key": "connection_logs.s3.enabled",
                        "Value": true
                    },
                    {
                        "Key": "connection_logs.s3.bucket",
                        "Value":  { "Ref": "ALBLogBucket"}
                    },
                    {
                        "Key": "connection_logs.s3.prefix",
                        "Value": {"Ref":"ConnectionLogPrefix"} 
                    },
                    {
                        "Key": "routing.http.xff_header_processing.mode",
                        "Value": "append"
                    }
                ],
                "Tags": [
                    {
                    "Key": "Name",
                    "Value": {"Fn::Sub": "${AWS::StackName}-ALB"}
                    }
                ]
            },
            "DependsOn":[
                "ALBLogBucket",
                "ALBLogBucketPolicy"
            ]
        },
        "TGAGS": {
            "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
            "Properties": {
                "Name": "TG-AGS",
                "Port": {"Ref": "AGSExternalPort"},
                "Protocol": {"Ref":"HealthCheckProtocol"},
                "TargetType": "instance",
                "VpcId": { "Ref": "VPCId" },
                "HealthCheckIntervalSeconds": { "Ref": "HealthCheckIntervalSeconds"},
                "HealthCheckPath": {"Ref":"AGSHealthCheckPath"},
                "HealthCheckProtocol": {"Ref": "HealthCheckProtocol"},
                "HealthCheckTimeoutSeconds":{"Ref":"HealthCheckTimeoutSeconds"},
                "HealthCheckPort":{"Ref":"AGSInternalPort"},
                "Targets":[
                    {
                        "Id": {"Ref":"AGSInstanceId"},
                        "Port": {"Ref":"AGSInternalPort"}
                    }
                    
                ]
            }
        },
        "TGPortal": {
            "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
            "Properties": {
                "Name": "TG-Portal",
                "Port": {"Ref": "PortalExternalPort"},
                "Protocol": "HTTPS",
                "TargetType": "instance",
                "VpcId": { "Ref": "VPCId" },
                "HealthCheckIntervalSeconds": { "Ref": "HealthCheckIntervalSeconds"},
                "HealthCheckPath": { "Ref": "PortalHealthCheckPath"},
                "HealthCheckProtocol": "HTTPS",
                "HealthCheckTimeoutSeconds":{"Ref":"HealthCheckTimeoutSeconds"},
                "HealthCheckPort":{"Ref":"PortalInternalPort"},
                "Targets":[
                    {
                        "Id": {"Ref":"PortalInstanceId"},
                        "Port": {"Ref":"PortalInternalPort"}

                    }
                    
                ]
            }
        },
        "TGGeoProc": {
            "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
            "Properties": {
                "Name": "TG-GeoProc",
                "Port": {"Ref": "AGSExternalPort"},
                "Protocol": {"Ref":"HealthCheckProtocol"},
                "TargetType": "instance",
                "VpcId": { "Ref": "VPCId" },
                "HealthCheckIntervalSeconds": { "Ref": "HealthCheckIntervalSeconds"},
                "HealthCheckPath": { "Ref": "PortalHealthCheckPath"},
                "HealthCheckProtocol": {"Ref": "HealthCheckProtocol"},
                "HealthCheckTimeoutSeconds":{"Ref":"HealthCheckTimeoutSeconds"},
                "HealthCheckPort":{"Ref":"AGSInternalPort"},
                "Targets":[
                    {
                        "Id": {"Ref":"GeoProcInstanceId"},
                        "Port": {"Ref":"AGSInternalPort"}

                    }
                    
                ]
            }
        },
        "TGImageProc": {
            "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
            "Properties": {
                "Name": "TG-ImageProc",
                "Port": {"Ref": "AGSExternalPort"},
                "Protocol": {"Ref":"HealthCheckProtocol"},
                "TargetType": "instance",
                "VpcId": { "Ref": "VPCId" },
                "HealthCheckIntervalSeconds": { "Ref": "HealthCheckIntervalSeconds"},
                "HealthCheckPath": { "Ref": "AGSHealthCheckPath"},
                "HealthCheckProtocol": {"Ref": "HealthCheckProtocol"},
                "HealthCheckTimeoutSeconds":{"Ref":"HealthCheckTimeoutSeconds"},
                "HealthCheckPort":{"Ref":"AGSInternalPort"},
                "Targets":[
                    {
                        "Id": {"Ref":"ImageProcInstanceId"},
                        "Port": {"Ref":"AGSInternalPort"}

                    }
                    
                ]
            }
        },
        "ListenerHTTPS":{
            "Type": "AWS::ElasticLoadBalancingV2::Listener",
            "Properties":{
                "LoadBalancerArn": { "Ref": "ALB"},
                "Port": 443,
                "Protocol": "HTTPS",
                "SslPolicy": "ELBSecurityPolicy-TLS13-1-2-2021-06",
                "Certificates":[
                    {"CertificateArn": { "Ref": "CertificateArn"}}
                ],
                "DefaultActions": [
                {
                    "Type": "redirect",
                    "RedirectConfig":{
                        "Protocol": "HTTPS",
                        "Host": "#{host}",
                        "Path": "/arcgis/home/",
                        "StatusCode": "HTTP_301"
                    }
                }

                ]
                    

            }
        },
        "ListenerFederation6443":{
            "Type": "AWS::ElasticLoadBalancingV2::Listener",
            "Properties":{
                "LoadBalancerArn": { "Ref": "ALB"},
                "Port": {"Ref": "AGSFederationExternalPort"},
                "Protocol": "HTTPS",
                "SslPolicy": "ELBSecurityPolicy-TLS13-1-2-2021-06",
                "Certificates":[
                    {"CertificateArn": { "Ref": "CertificateArn"}}
                ],
                "DefaultActions": [
                {
                    "Type": "forward",
                    "ForwardConfig":{
                        "TargetGroups": [
                            {
                                "TargetGroupArn": { "Ref": "TGAGS" }
                            }
                        ]
                    }
                }

                ]
                    

            }
        },
        "ListenerFederation7443":{
            "Type": "AWS::ElasticLoadBalancingV2::Listener",
            "Properties":{
                "LoadBalancerArn": { "Ref": "ALB"},
                "Port": {"Ref": "PortalFederationExternalPort"},
                "Protocol": "HTTPS",
                "SslPolicy": "ELBSecurityPolicy-TLS13-1-2-2021-06",
                "Certificates":[
                    {"CertificateArn": { "Ref": "CertificateArn"}}
                ],
                "DefaultActions": [
                {
                    "Type": "forward",
                    "ForwardConfig":{
                        "TargetGroups": [
                            {
                                "TargetGroupArn": { "Ref": "TGPortal" }
                            }
                        ]
                    }
                }

                ]
                    

            }
        },
        "ListenerRuleAGS": {
            "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
            "DependsOn": "ALB",
            "Properties": {
                "Actions": [
                    {
                        "Type": "forward",
                        "TargetGroupArn": { "Ref": "TGAGS" }
                    }
                ],
                "Conditions": [
                    {
                        "Field": "path-pattern",
                        "Values": [
                            "/arcgis/rest/*",
                            "/arcgis/admin/*",
                            "/arcgis/services/*"
                            
                            
                        ]
                    }
                ],
              "ListenerArn": { "Fn::GetAtt": ["ListenerHTTPS", "ListenerArn"] },
              "Priority": 20
            }
        },
        "ListenerRulePortal": {
            "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
            "DependsOn": "ALB",
            "Properties": {
                "Actions": [
                    {
                        "Type": "forward",
                        "TargetGroupArn": { "Ref": "TGPortal" }
                    }
                ],
                "Conditions": [
                    {
                        "Field": "path-pattern",
                        "Values": [
                            
                            "/portal/*",
                            "/arcgis/home/*",
                            "/arcgis/sharing/*",
                            "/arcgis/portaladmin/*",
                            "/arcgis/portaladmin?*"

                            
                        ]
                    }
                ],
                "ListenerArn": { "Fn::GetAtt": ["ListenerHTTPS", "ListenerArn"] },
                "Priority": 10

            }
        },
        "ListenerRuleGeoProc": {
            "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
            "DependsOn": "ALB",
            "Properties": {
                "Actions": [
                    {
                        "Type": "forward",
                        "TargetGroupArn": { "Ref": "TGGeoProc" }
                    }
                ],
                "Conditions": [
                    {
                        "Field": "path-pattern",
                        "Values": ["/geoprocessing/*"]
                    }
                ],
                "ListenerArn": { "Fn::GetAtt": ["ListenerHTTPS", "ListenerArn"] },
                "Priority": 30

            }
        },
        "ListenerRuleImageProc": {
            "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
            "DependsOn": "ALB",
            "Properties": {
                "Actions": [
                    {
                        "Type": "forward",
                        "TargetGroupArn": { "Ref": "TGImageProc" }
                    }
                ],
                "Conditions": [
                    {
                        "Field": "path-pattern",
                        "Values": ["/image/*"]
                    }
                ],
                "ListenerArn": { "Fn::GetAtt": ["ListenerHTTPS", "ListenerArn"] },
                "Priority": 40

            }
        },
        "ListenerRuleRedirectArcGISManagerToVoid":{
            "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
            "DependsOn": "ALB",
            "Properties": {
                "Actions": [
                    {
                        "Type": "fixed-response",
                        "FixedResponseConfig": {
                            "StatusCode": "403"                        
                        }
                    }
                ],
                "Conditions": [
                    {
                        "Field": "path-pattern",
                        "Values": [
                            "/arcgis/manager*",
                            "/arcgis/admin/*"
                        ]
                    }
                ],
                "ListenerArn": { "Fn::GetAtt": ["ListenerHTTPS", "ListenerArn"] },
                "Priority": 70
            }  
        }
         
    },
    "Outputs":{

    }
}

 

 

Anything else I should know or any obvious mistakes/missteps here?

0 Kudos
1 Solution

Accepted Solutions
TraeTimmerman
Occasional Contributor

Hey @KarlC ,

I don't have time review your entire CFT, but a couple things from your initial description:

  1. Federation: yes, you can federate both/all of your ArcGIS Server sites with Portal for ArcGIS
  2. Context Paths: the routing you mentioned looks mostly correct, however, all traffic routed to the Portal for ArcGIS site should use the /portal context path, e.g., /portal/portaladmin. I would recommend setting up your listener to forward all traffic to the target group based on a match with the first constant.
    1. gis.company.com/portal -> portal target group
    2. gis.company.com/arcgis -> arcgis server target group
    3. gis.company.com/img -> image server target group
  3. ArcGIS Server's internal web server listens on 6443 and Portal for ArcGIS' internal web server listens on 7443, so configure your target groups accordingly.
  4. Out of the box, the components have a self-signed certificate. Best practice is to swap that out with a CA-signed or domain-issued certificate. 

Hope this helps.

View solution in original post

0 Kudos
1 Reply
TraeTimmerman
Occasional Contributor

Hey @KarlC ,

I don't have time review your entire CFT, but a couple things from your initial description:

  1. Federation: yes, you can federate both/all of your ArcGIS Server sites with Portal for ArcGIS
  2. Context Paths: the routing you mentioned looks mostly correct, however, all traffic routed to the Portal for ArcGIS site should use the /portal context path, e.g., /portal/portaladmin. I would recommend setting up your listener to forward all traffic to the target group based on a match with the first constant.
    1. gis.company.com/portal -> portal target group
    2. gis.company.com/arcgis -> arcgis server target group
    3. gis.company.com/img -> image server target group
  3. ArcGIS Server's internal web server listens on 6443 and Portal for ArcGIS' internal web server listens on 7443, so configure your target groups accordingly.
  4. Out of the box, the components have a self-signed certificate. Best practice is to swap that out with a CA-signed or domain-issued certificate. 

Hope this helps.

0 Kudos